CSDL Explorer
A terminal explorer for OData CSDL metadata. Parse any $metadata XML and discover entities, properties, relationships, and field attributes — then query live OData services directly from the terminal.
Why
OData services expose their schema through $metadata but those XML documents can be enormous (10MB+, 700+ entities). This tool parses the CSDL and gives you fast, searchable access to every entity, property, navigation relationship, and annotation.
Particularly useful with SAP SuccessFactors, where documentation says "Worker Category" but the actual field is customString17.
Install
pip install csdl-explore # Rich REPL
pip install csdl-explore[tui] # + Textual TUI (tree nav, split panes, query builder)
Quick Start
csdl-explore metadata.xml # Interactive REPL
csdl-explore metadata.xml --tui # Full TUI
csdl-explore metadata.xml search contract # Search fields
csdl-explore metadata.xml entity EmpJob # Entity details
csdl-explore metadata.xml tree EmpJob # Visual tree
Features
- Rich REPL — colored tables, visual trees, entity comparison
- Textual TUI — tree navigation, tabbed views, split panes, navigation graph
- Live OData queries — visual query builder with filter, select, expand, orderby
- SAP SuccessFactors auth — Bearer, Basic, OAuth2 SAML
- Picklist exploration — usage analysis, impact analysis, live value fetching
- Headless mode — all commands work non-interactively for scripts and AI assistants
- Search — across entity names, property names, labels, and picklist values
- Themes — Terminal green + amber accent (default), Classic
Interactive REPL
$ csdl-explore metadata.xml
╭─────────────────────────────────────────╮
│ CSDL Explorer │
│ Loaded 735 entities │
╰─────────────────────────────────────────╯
csdl> search contract
┏━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓
┃ Type ┃ Entity ┃ Match ┃ Details ┃
┣━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━╋━━━━━━━━━┫
┃ PROP ┃ EmpJob ┃ .contractType ┃ String ┃
┃ NAV ┃ EmpJob ┃ .contractTypeNav┃ ┃
┗━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━━━━━┛
csdl> tree EmpJob
EmpJob
├── Keys
│ ├── seqNumber Edm.Int64
│ ├── startDate Edm.DateTime
│ └── userId Edm.String
├── Properties (45)
│ ├── businessUnit Edm.String
│ └── ... and 43 more
├── Custom Fields (24)
│ ├── customString7 Edm.String
│ └── ... and 23 more
└── Navigation (8)
├── employmentNav -> EmpEmployment
└── ... and 7 more
Commands
| Command | Aliases | Description |
|---|---|---|
entities |
List all entity types | |
entity <name> |
e |
Show all properties of an entity |
tree <name> |
t |
Show entity as visual tree |
search <term> |
s |
Search entities and properties |
custom <entity> |
c |
Show custom fields |
nav <entity> |
Show navigation properties | |
diff <e1> <e2> |
Compare two entities | |
path <entity> |
paths |
Suggest JSON paths |
emp / per |
List Emp*/Per* entities (SAP SF) | |
picklists |
List all picklists (JSON) | |
picklist <name> |
pk |
Fetch picklist values from live API |
batch-picklists <entity> |
Fetch all picklist values for an entity | |
query <entity> |
Execute OData query | |
model [entities] |
Show data model overview |
OData Queries
csdl-explore metadata.xml query EmpJob \
--select "userId,company,businessUnit" \
--filter "company eq 'ACME'" \
--top 10
Query flags: --select, --filter, --orderby, --orderby-dir, --top, --expand, --asof-date, --from-date, --to-date
CLI Options
| Option | Description |
|---|---|
--tui |
Launch full Textual TUI |
--base-url <url> |
OData service base URL |
--auth-type <type> |
none, bearer, basic, oauth2 |
--format <fmt> |
table, json, json-compact, csv |
--wide |
Full column widths |
--filter <pattern> |
Filter properties by name glob |
--json-only |
JSON output only (for scripts) |
Connecting to an OData Service
CSDL Explorer can query live OData services — not just parse static metadata. You need a base URL and credentials.
Auth Types
| Type | Description |
|---|---|
none |
No authentication |
bearer |
Static Bearer token in the Authorization header |
basic |
HTTP Basic Auth (username + password) |
oauth2 |
SAP SAML Bearer assertion flow (IDP → token exchange) |
Inline Credentials
Pass connection flags directly:
# Bearer token
csdl-explore metadata.xml --base-url https://api.example.com/odata/v2 \
--auth-type bearer --bearer-token YOUR_TOKEN \
query EmpJob --top 5
# Basic auth
csdl-explore metadata.xml --base-url https://api.example.com/odata/v2 \
--auth-type basic --username admin --password secret \
picklist ecJobCode
.env File (Recommended)
Place a .env file next to your metadata XML with the same stem name. If your metadata is metadata.xml, create metadata.env:
SAP_BASE_URL=https://api.example.com/odata/v2
SAP_AUTH_TYPE=bearer
SAP_BEARER_TOKEN=your-token-here
Then just run commands — credentials are loaded automatically:
csdl-explore metadata.xml query EmpJob --top 5
csdl-explore metadata.xml picklist ecJobCode
CLI flags override .env values when both are present.
All .env keys:
| Key | Description |
|---|---|
SAP_BASE_URL |
OData service base URL |
SAP_AUTH_TYPE |
none, bearer, basic, oauth2 |
SAP_BEARER_TOKEN |
Bearer token |
SAP_USERNAME |
Basic auth username |
SAP_PASSWORD |
Basic auth password |
SAP_IDP_URL |
OAuth2: SAML IDP endpoint |
SAP_TOKEN_URL |
OAuth2: Token exchange endpoint |
SAP_CLIENT_ID |
OAuth2: Client ID |
SAP_USER_ID |
OAuth2: User ID for assertion |
SAP_COMPANY_ID |
OAuth2: Company ID |
SAP_PRIVATE_KEY |
OAuth2: Private key for signing |
SAP_GRANT_TYPE |
OAuth2: Grant type |
TUI Connection
In the Textual TUI, credentials are configured through the auth modal (accessible from the Query tab). The TUI also reads the .env file on startup, so if you've already configured it for the CLI, the TUI picks it up automatically.
Textual TUI
pip install csdl-explore[tui]
csdl-explore metadata.xml --tui
| Key | Action |
|---|---|
/ |
Focus search |
Escape |
Clear search |
t |
Toggle tree sidebar |
Ctrl+T |
Cycle theme |
Ctrl+W |
Close current tab |
? |
Help |
q |
Quit |
Python API
from csdl_explore import CSDLExplorer
from pathlib import Path
explorer = CSDLExplorer.from_file(Path("metadata.xml"))
# Search
for r in explorer.search("worker"):
print(f"{r.entity}.{r.property}: {r.prop_type}")
# Entity details
entity = explorer.get_entity("EmpJob")
for prop in entity.properties.values():
print(f"{prop.name}: {prop.type} (label={prop.label})")
# Custom fields, comparison, picklist usage
fields = explorer.get_custom_fields("EmpJob")
comp = explorer.compare_entities("EmpCompensation", "EmpPayCompRecurring")
usage = explorer.get_picklist_usage()
Supported Services
Any OData service with CSDL $metadata. Tested with:
- SAP SuccessFactors (full annotation support: labels, picklists, CRUD flags)
- SAP S/4HANA OData
- Standard OData v2/v3 services
License
MIT
Release files for csdl-explore 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| csdl_explore-0.2.0.tar.gz | 3.1 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| csdl_explore-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.2 MB
Release files / csdl_explore-0.2.0.tar.gz
| Download URL | csdl_explore-0.2.0.tar.gz |
|---|---|
| Size | 3.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4ed8f32e2af4427984763b87c23ee75210c9a1af8f4aaa8f52d7ebf553a5845f
|
|
BLAKE2b-256 checksum How to use checksums |
955a897bf36cad84d9d58288c196e600c2ff26fa143c27c748716f109cb64bb1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.3
|
Release files / csdl_explore-0.2.0-py3-none-any.whl
| Download URL | csdl_explore-0.2.0-py3-none-any.whl |
|---|---|
| Size | 78.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b5021e3462dfd6a410242f9d2f7ca02c178ebb24ee392a5f25b764b936d21279
|
|
BLAKE2b-256 checksum How to use checksums |
689b418fa0d78ace582831d612a8a7ee4cef6982ca017de65b6e7241bda0f287
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.3
|