mcpField
S
schema-explorer

schema-explorer

github.com/datexData & databases113 installs
Repository

About schema-explorer

schema-explorer is a skill for discovering OData schemas through dxs commands and producing field mappings. It helps data practitioners identify entities, relationships, properties, enum values, and available index metadata for a connection.

Key features

  1. 01Search and describe entities
  2. 02Explore entity relationships
  3. 03Inspect fields and navigation properties
  4. 04Resolve enum values
  5. 05Check index metadata

Use cases

  1. 01Mapping fields for a data source
  2. 02Finding entities behind business concepts
  3. 03Checking expansion paths before OData queries

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/datex/skills --skill schema-explorer

After installing, type/schema-explorerin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

evals/
evals.json
references/
batch-syntax.md
subagent-template.md

Details

From the project README

Schema Explorer

OData schema discovery using dxs schema commands.

References

Footprint connections — check the entity-expert skill first

If the connection is a FootPrintApi connection (Footprint WMS data — Tasks, Shipments, ArchivedShippingLicensePlateContents, LicensePlate, Lot, Material, etc.), check the footprint-entity-expert skill before exploring schema. It captures navigation chains, business-rule filters, and weight calculations that aren't visible in OData metadata — and tells you which entity actually backs a given business concept (e.g., "shipped inventory" → ASLPC, not Shipments). Skip this for non-Footprint connections.

Resolving the Connection

Schema commands require a connection ID (-c <id>). The user may or may not provide one explicitly.

If the user provides a connection ID or name — use it directly. Examples: "use connection 9", "use the Prod connection".

If the user says something like "in Prod" or "on the Demo environment" — find the matching FootprintApi connection:

  1. dxs auth status — get the authenticated organization ID
  2. dxs organization connection list --org <org_id> — list all connections
  3. Filter to apiConnectionTypeName: FootPrintApi and match the name

If the user doesn't specify a connection at all — list the available FootprintApi connections and ask which one to use:

  1. dxs auth status — get the org ID
  2. dxs organization connection list --org <org_id> — list connections
  3. Show the user just the FootprintApi connections (filter out MongoDB, AMQP, SFTP, etc.) and ask them to pick one

Input/Output Contract

Input: Connection ID + search keywords

Output: field-mapping.md file with this structure:

# Schema: <EntityName>

## Connection
- Connection ID: <id>
- Namespace: <namespace>

## Primary Entity
- Entity Set: <name>
- Entity Type: <namespace.type>
- Keys: <key fields and types>

## Fields

### Root Fields
| Field | Type | Notes |
|-------|------|-------|

### Navigation Properties
| Nav Property | Target Type | Cardinality | Key Fields |
|-------------|-------------|-------------|------------|

### Expanded Fields (via $expand)
| Path | Type | Source Entity | Binding |
|------|------|---------------|---------|

Mark the **Binding** column:
- `flat` — field is reachable through single navigation properties; safe for flat DataSet fields
- `⚠ collection` — path crosses a collection navigation property (1:N cardinality); requires flow datasource flattening or child dataset with `CommandText` deep path. Will silently render blank if added as a flat DataSet field.

## Composite Keys / Special Notes

Command Selection Rule

Single request → use the direct command. Multiple independent requests → combine into ONE batch.

  • If you only need one operation (e.g., one search, one describe), use the direct command — don't wrap it in dxs schema batch with a single --request.
  • If you need 2+ independent operations, combine ALL of them into a single dxs schema batch call. Don't split independent requests across multiple batch calls — each call is a separate HTTP roundtrip.
  • Only split into a second call when results from the first call determine what to query next.
  • Scope every list command. entities, actions, properties and indexes return the whole model when given no filter — on a real connection that is tens to hundreds of KB per call, and indexes is the largest of them. Each has its own narrowing flag: --entity-type on properties / navigation-properties, --entity-set and --covering on indexes, --search and --filter on all of them. Page with -n and --skip; when all you need is the size, use --count.

Workflow

  1. Search for entities — Combine ALL keyword searches into one batch:

    dxs schema batch -c <id> \
      --request 'search shipment' \
      --request 'search order' \
      --request 'search warehouse' \
      --request 'search carrier'
    

    If you can guess entity set names, combine searches with describes in the same batch. Search results include the full qualified entity_type (e.g., Datex.FootPrint.Api.Warehouse) — save these for --entity-type flags in later steps.

    For a single keyword, use the direct command: dxs schema search "keyword" -c <id>.

  2. Describe entities and relationships — Combine ALL entity descriptions and relationship exploration into one batch. Use --compact --no-udf on describe-entity and --depth 2 on relationships:

    dxs schema batch -c <id> \
      --request 'describe-entity Shipments --compact --no-udf' \
      --request 'describe-relationships Shipments --depth 2' \
      --request 'describe-entity Orders --compact --no-udf' \
      --request 'describe-relationships Orders --depth 2' \
      --request 'describe-entity ShipmentLines --compact --no-udf' \
      --request 'describe-relationships ShipmentLines --depth 2'
    

    Use --compact for initial exploration. If you need full details on specific fields later, use --select Field1,Field2 to describe only those properties.

  3. Scan related entity fields — Combine ALL related entity scans into one batch. For each navigation property target you plan to $expand, use describe-entity --compact --no-udf:

    dxs schema batch -c <id> \
      --request 'describe-entity Materials --compact --no-udf' \
      --request 'describe-entity Lots --compact --no-udf' \
      --request 'describe-entity Addresses --compact --no-udf' \
      --request 'describe-entity Contacts --compact --no-udf' \
      --request 'describe-entity Warehouses --compact --no-udf' \
      --request 'describe-relationships Warehouses --depth 2'
    

    Skip entities already covered by describe-relationships --depth 2 in step 2 — depth-2 output includes nested nav property names and target types. Only scan entities where you need the scalar field list for $select clauses.

    When you only have the type ID (e.g., Datex.FootPrint.Api.Warehouse from relationship output) and don't know the entity set name, use properties --entity-type Namespace.Type instead — it accepts the full qualified type directly. Use navigation-properties --entity-type alongside it if you also need $expand paths.

    Note: If you need $expand paths for a related entity (what can be expanded from it), those are in its navigation_properties output — describe-entity --compact includes both scalar properties and nav properties.

  4. Resolve enum values — When a property's type doesn't start with Edm., it's an enum or complex type. Combine all enum lookups into one batch (or use a single direct command if only one):

    dxs schema batch -c <id> \
      --request 'describe-enum Datex.FootPrint.Api.Statuses' \
      --request 'describe-enum Datex.FootPrint.Api.OrderTypes'
    

    This tells you what filter values mean (e.g., StatusId eq 1 → "Active").

  5. Verify expansion paths — Invoke the odata-execution skill to confirm nav properties return data ($top=1 + $expand). Some entities have nav properties with NULL foreign keys resulting in empty expansions.

    What to verify:

    • Deep paths (depth >= 2, e.g., OrderLine.Material.Description) — higher failure risk
    • Paths through nullable foreign keys (e.g., ShipToContact when ShipToContactId is nullable)
    • Collection-through-collection expansions (e.g., ShipmentLines($expand=OrderLine(...)))
    • Any path you are not confident about

    Safe to skip: Single-level expansions to lookup entities (Status, Type, Category patterns) where the foreign key is non-nullable. These are structurally guaranteed by the schema.

    Note gaps and suggest alternate query paths (e.g., querying the related entity directly with a filter). Also test lambda operators (any()/all()) if you need to filter within expanded collections.

  6. Build field mapping table — Compile all discovered fields into the field mapping template, separating root fields from navigation properties and expanded fields.

    Address gaps explicitly. If the user asked about a concept (e.g., "success/failure", "late orders", "priority") and the schema has no direct field for it, say so in the field mapping. Explaining what doesn't exist is as valuable as documenting what does — it saves the user from searching for something that isn't there, and lets them decide on a workaround (derived calculation, external lookup, etc.).

Index Awareness

Needs a dxs newer than 0.5.5 — these commands merged after the 0.5.5 tag. On an older build dxs schema indexes does not exist and describe-entity returns no indexes key. That absence is the CLI's, not the metadata's, and says nothing about the storage.

The metadata reports the physical database indexes behind each entity set. Check them whenever the schema you are mapping will be filtered or sorted on a large entity set — an unindexed predicate is the usual cause of a datasource that times out in production but looks fine on a small dev dataset.

describe-entity already returns the entity set's indexes at no extra HTTP call, so if you ran step 2 you have them — --compact, --no-udf and --properties-only all keep them. Reach for the indexes command only for cross-entity questions:

dxs schema batch -c <id> \
  --request 'indexes --entity-set Shipments' \
  --request 'indexes --covering ModifiedSysDateTime'

--covering COLUMN is shorthand for Columns/any(c: c/Name eq 'COLUMN') — "is this column indexed anywhere?" without hand-writing the lambda.

Not every Footprint server reports indexes at all. The schema service has to expose an Indexes entity set, and older versions do not. The CLI probes for it once per connection (cached 24h) and adapts rather than failing the request: indexes and describe-index refuse with DXS-SCHEMA-015, and describe-entity drops the optional expand, returning no indexes key. Direct describe-entity says why in a warning; inside a batch it says nothing — the key is simply absent, so on the batch path treat a missing indexes key as "ask this connection directly before concluding anything". In a batch, index-only requests are answered with that error locally and never sent, so one unsupported request cannot take the batch down with it. A capability gap is a fact about the server version and not a fourth kind of index finding: it says nothing whatever about the storage. Report it as "this server does not report index metadata", never as "no indexes".

Reading the output — three traps:

  • key_ordinal must be 1 for a filter on that column alone to seek. An index on (StatusId, AccountId) does not help a filter on AccountId by itself.
  • is_included: true is not filterable. The column rides along to satisfy a $select without a lookup; filtering on it alone still scans.
  • No indexes reported means unknown, not none. An empty result — indexes: [] on describe-entity, or an empty indexes list — means the metadata is silent about that entity set, not that its storage has none. Entity sets in this state are always read-only in the API, but several look like ordinary keyed tables, so an index probably exists and just is not reported. Do not conclude a filter there scans — or seeks. Say "no index information" in the mapping.

An empty --covering result has three readings, and scoping rules out only one of them. --entity-set rules out misspelled: a column that does not exist on that entity type fails with DXS-SCHEMA-014 instead of coming back empty. It does not rule out silent — if the entity set reports no indexes at all, a scoped miss is still "unknown", exactly as in the third trap. The definitive reading — "this column is not indexed, a filter on it scans" — needs both: scoped, and an entity set that reports at least one index. Unscoped there is no entity type to validate the name against, so unindexed-everywhere and misspelled are indistinguishable. A model-wide --covering is fine for "where is this column indexed?", but the moment the answer decides a $filter, re-run it scoped:

dxs schema indexes -c <id> --entity-set Shipments --covering ModifiedSysDateTime

Let the CLI say which of the three you hit instead of inferring it — but know where it puts the answer, because that differs by path:

PathWhere the wording lands
dxs schema indexes, empty and scoped by --entity-set / --coveringmetadata.index_hint
indexes inside dxs schema batch, same casethat request's warnings[]
describe-entity with indexes: [], directmetadata.index_hint
describe-entity with indexes: [], in a batchthat request's warnings[]
An unscoped indexes list that comes back emptynothing — scope it before reading anything into it
No indexes key at alldirect describe-entity: a warning naming the server. In a batch, or on a CLI older than the feature: nothing — not a finding about the entity set either way

Every empty result you can actually reason from carries its wording; the one that does not is the unscoped miss, which is also the one that proves least.

When an intended filter column has no leading-column index, say so in the Notes column of the root-field table, next to the field. That is a real constraint on the datasource design, not a footnote.

A guarded filter can vanish, taking the index with it. Every way dxs datasource generate parameterizes a filter wraps the predicate in a $utils.isDefined() guard, so an absent parameter at runtime drops it and the query you just index-checked scans the whole table. No generate flag emits a required, unguarded filter — --detect-params least of all, despite the name.

So an index check on a parameterized filter is only as good as the binding behind it. Report the scoping parameter alongside the index finding and hand off to datasource-creator: its references/odata-datasources.md → "The index check is undone by a guarded scoping filter" carries the mechanics and the two ways to close the gap.

Subagent Usage

Schema exploration can generate significant output. Delegate to a subagent to keep the main context clean. See references/subagent-template.md for the prompt template.

Write output to the artifact directory:

  • 01-schema-exploration.md — raw schema discovery output
  • 02-field-mapping.md — completed field mapping table

describe-entity Flags

describe-entity does NOT support --concise. See references/batch-syntax.md for the full display flags table (--compact, --no-udf, -P, -N, --prop-top, --select). All flags work in batch: --request 'describe-entity Orders --compact --no-udf'

Additional Rules

Rules already embedded in the workflow steps above are not repeated here. See references/batch-syntax.md for the full anti-patterns table.

  • Always check the keys: section — some entities have composite keys (no Id field)
  • describe-properties takes one property name at a time — not comma-separated; for bulk discovery use describe-entity --compact --no-udf or batch multiple calls
  • A DXS-API-CONN / DXS-API-TLS / DXS-API-TIMEOUT / DXS-API-NET error from any schema command is a network failure, not a bad entity name — follow the payload's suggestions[]; see configuration-roundtrip.md → Exit 141 and transport errors
  • Batch eliminates the "run sequentially" constraint — unlike parallel tool calls (which abort siblings on failure), batch handles individual request failures gracefully

Comments

Markdown supported