TeamcenterKnowledge

Skills

TC Type Discovery And Census

Skill tc-type-discovery-and-census. Discover what business object types are deployed on ANY Teamcenter tier at runtime, what attributes they carry, and how many instances of each actually exist - without a BMIDE export and without hardcoding anything per deployment. Covers the two SOA operations that enumerate the live type model, the delta-protocol trap that makes getTypeDescriptions2 return less every time you call it (and eventually nothing, which reads as "type not deployed"), the false-zero trap where a populated type reports nFound 0 because it has no object_name, the static predictor that separates a trustworthy zero from a meaningless one, proof that Teamcenter has no 500-row ceiling (that clamp lives in tc-mcp's client), and the property-tier signal that cuts 190 attributes down to the handful worth reporting on. Use for any "what types exist here", "how many of X are there", data-model discovery, BI/analytics feed, or type-picker work.

Provenance: EXERCISED end to end on 2026-08-20 against live TC2606 (siemensdc, Hyper-V VM, ordinary non-DBA account ed), while building the Teamcenter Whisperer's /data layer. Every number below came back from a real call. Lines marked DERIVED did not.

0. The one-paragraph version

Two SOA operations give you a live tier's type model with no BMIDE export and nothing hardcoded: Core-2010-04-DataManagement/findDisplayableSubBusinessObjectsWithDisplayNames enumerates the deployed types, and Core-2015-10-Session/getTypeDescriptions2 gives their properties. Counting instances is a third call and is cheap. All three have traps that return a plausible answer rather than an error, and two of them fail in the direction that reads as "this type does not exist". On TC2606 the useful result was: 575 displayable types deployed under Item, of which 49 hold any data at all. That ratio is the point of doing this: a type picker offering 575 entries is unusable, one offering 49 is a product.

1. Enumerating the deployed types

EXERCISED.

Core-2010-04-DataManagement/findDisplayableSubBusinessObjectsWithDisplayNames
{"input": [{"boTypeName": "Item", "exclusionBOTypeNames": []}]}

Returns output[].displayableBOTypeNames[], each carrying boName (wire name), boDisplayName (what a human should see) and boParents (the full parent chain). 575 entries for Item on TC2606, reflecting exactly which templates are installed: the Crt0*, Psi0*, Aqc0* and Seg0* types are present only because those templates are deployed.

Teamcenter marks deprecation in the DISPLAY NAME, of all places: "Deprecated. See Support Announcement 2312. Visio Diagram", "System Model @deprecated". Match on "deprecat" case-insensitively in boDisplayName and you get a free signal for steering people off dying types.

Core-2010-04-DataManagement/getAvailableTypesWithDisplayNames is NOT the same thing and looks like it should be. Asked with baseClass: "WorkspaceObject" it returned exactly one entry, WorkspaceObject itself. It answers a narrower question ("available to create in this context") in the same shape a full enumeration would take. Use findDisplayableSubBusinessObjects....

2. getTypeDescriptions2 is a DELTA protocol, not an idempotent read

EXERCISED, and this one would have shipped as a bug.

Core-2015-10-Session/getTypeDescriptions2
{"typeNames": ["Requirement"]}

Same session, in this order:

asked returned
["Requirement"] 7: Requirement, BusinessObject, Item, POM_application_object, POM_object, SpecElement, WorkspaceObject
["Fnd0LogicalBlock"] 2: Fnd0LogicalBlock, Fnd0SEBlock (its other ancestors omitted: already sent)
["Requirement","Fnd0LogicalBlock"] 2, no ancestors at all

The server sends a type once per session and assumes the client kept it. So the same request returns less and less, and eventually nothing. A response that omits your type is indistinguishable from "this type is not deployed", so any code that calls this twice and expects the same answer fails in the direction that reads as a missing type.

Route every describe through ONE session-scoped cache. And read the ancestor chain from the descriptor's own typeHierarchy member ("Requirement,SpecElement,Item,WorkspaceObject, POM_application_object,POM_object,BusinessObject"), which is stable, never from which types the call happened to return, which is session history.

Two more things about this operation:

  • Passing an options member of ANY shape returned HTTP 500 on 2606 (tried as a list of {name, values}, as a dict, and as an empty list). Omitting it entirely works. The XSD documents options extensively, so this reads as a payload bug and is not one.
  • A misspelled type and an uninstalled type are omitted identically, with no partialError. This is survivable if every name you pass came from the enumeration in section 1, because then you never ask about a name the tier did not just give you. That is a structural fix and it beats sending a known-real control on every call.

3. The false zero: a populated type that reports nFound 0

EXERCISED. This is the trap that makes a naive census confidently wrong.

The OOTB General... saved query matches on name. A type with no object_name property cannot be matched by it, so it returns nFound 0 even when populated instances exist. Three different facts collapse to one identical answer: not deployed, genuinely empty, and not visible to this instrument.

type nFound has object_name
Folder 148 yes real
Item 291 yes real
Requirement 971 yes real
ItemRevision 292 yes real
Att0MeasureValueDbl 0 no FALSE zero
UnitOfMeasure 0 no FALSE zero

A live Att0MeasureValueDbl instance has been read in this workspace carrying att0Value = 0.056, so that zero is not an empty population.

The predictor: does the type declare object_name in its propertyDescriptors? It is static, per type, available from section 2, and requires no instance. That last part is what makes it usable: you cannot fetch an instance of a type you cannot find.

object_string is NOT the discriminator. It is declared on the broken types too. This corrects an earlier workspace note that blamed object_string; the distinguishing property is object_name.

⇒ Emit an outcome, never a bare number: counted, empty (zero AND nameable, so trustworthy), uncountable (count is null and the reason is given), failed (the instrument should have worked and did not, which is deliberately distinct from all three).

4. Counting is cheap, and there is NO 500-row ceiling

EXERCISED, and this corrects a long-standing workspace belief.

executeSavedQuery's cap applies to objects returned, not to nFound. Measured against the 971-row Requirement population:

limit asked nFound objects returned
1 971 1
500 971 500
1000 971 971
5000 971 971

Two consequences:

  1. limit=1 is a free exact count. A census over hundreds of types costs one small call each. All 575 types on TC2606 took ~31 seconds.
  2. No pagination is needed to read a full population. Ask for more than nFound and you get everything in one call.

The 500 is a clamp inside tc-mcp's tc_client.query_by_type (min(int(limit or 50), 500)), not a Teamcenter limit. Anything written to work around a "Teamcenter 500-row limit" is working around a client library. Call executeSavedQuery directly through call_operation to bypass it.

5. Cutting 190 attributes down to the ones worth showing

EXERCISED. Fnd0LogicalBlock carries 190 property descriptors and Requirement carries about 180. Offering those to a person choosing a chart axis is the same failure as offering all 575 types.

The strongest signal is where a property is DECLARED, and it costs nothing because getTypeDescriptions2 returns the ancestor chain in the same call. Compute, per property, the shallowest type that declares it:

  • declared on the type itself: what makes it that type
  • declared on a meaningful ancestor (Item, SpecElement, Fnd0SEBlock)
  • declared on a universal base (BusinessObject, POM_object, POM_application_object, WorkspaceObject): infrastructure present on every object in Teamcenter

That last group is acl_bits, pid, active_seq, revision_limit, checked_out_date, backup_date. It generalises where a blacklist of property names would not: a customer's own Xyz0Whatever type ranks correctly with nobody maintaining a list.

Worked result: all seven of Requirement's numeric properties are universal-base plumbing. That is not a gap in the ranking, it is true and useful. There is nothing on a Requirement worth summing, so you chart requirements by counting them across a categorical dimension.

Secondary signals, DERIVED from observed 2606 data rather than a published table, so treat as heuristics: lovCategory non-empty means the property is LOV-backed and therefore categorical by construction, which is the best "this is a dimension" marker available; valueType 3/4/5/7 are numeric, 2 is a date, 8 is a string, 6 boolean, 9 reference, 14 relation. A string property with maxLength in the thousands is a description, not an axis.

One narrow exception is needed, and live data is what revealed it. object_name is declared on WorkspaceObject, so pure inheritance ranking demotes it as infrastructure. The default columns for Seg0MissionPhase came back as current_id, fnd0OriginalLocationCode, fnd0PosBiasedVariantAvail with no object_name anywhere: technically correct and useless. Being defined on a universal base is an implementation detail of Teamcenter's type tree, not evidence that the name of a thing is uninteresting. Keep the promoted list SHORT (object_name, object_type, item_id, owning_user, last_mod_date, creation_date) and test that it stays short: every entry overrides evidence.

6. What this route CANNOT see

DERIVED, by comparison with saber-atlas, which solves the adjacent problem from a BMIDE bmide_generate_datamodel_doc_report HTML export parsed offline into static per-deployment JSON. That is richer and not portable: it needs a multi-GB export produced by an admin, per deployment, and cannot be pointed at an arbitrary live tier. Its SOA client never calls a type-discovery operation at all.

Absent from getTypeDescriptions2 and therefore from this route: GRM relation rules, naming rules, deep-copy rules, display/condition rules, and BMIDE extensions. If you need those, you need the export. If you need "point it at any Teamcenter and tell me what is there", you need this.

7. Operations that did NOT work, so nobody re-derives them

EXERCISED failures, all against live TC2606:

  • InternalQuery-2008-06-Finder/findObjectsByClassAndAttributes - exactly the right shape on paper (className as a required attribute, no name matching anywhere, empty filter returns the objects) and would sidestep section 3 entirely. Returns 214086 invalid syntax on every body tried. 214086 means the operation resolves and the body is wrong, not that the op is missing. Unsolved. Next step is capturing what Active Workspace itself sends (see tc-capture-awc-calls), not a fourth guess.
  • Query-2014-11-Finder/performSearch - Active Workspace's own search backend, and its documentation is explicit that maxToReturn = 0 returns "total results found and filters details" with no payload, which would give facet counts for many types in one call. Returns 214022 JSON parsing error. Two shape bugs were found and fixed along the way and it still fails: StringMap2 is a LIST of {key, value} pairs rather than a JSON object, and searchFilterFieldSortType and internalPropertyName are use="required" attributes that are easy to miss. Still unsolved. Same next step.
  • Query-2006-03-SavedQuery/findSavedQueries - 214086 on the body shape tried. Use getSavedQueries and filter client-side; it works and returned 635 queries on 2606.

Related skills

tc-query-discovery (the proven read set), tc-soa-payload-shapes (what 214022 and 214086 really mean, and the full-struct rule that both failures in section 7 are instances of), tc-capture-awc-calls (the right next move for both unsolved operations), diagnose-silent-failure (the general shape of everything in sections 2 and 3).


Generated from skills/tc-type-discovery-and-census/SKILL.md in the tc-automation-skills library, which is the canonical copy and also serves as the agent skill set for Teamcenter work.