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
optionsmember 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 documentsoptionsextensively, 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:
limit=1is a free exact count. A census over hundreds of types costs one small call each. All 575 types on TC2606 took ~31 seconds.- No pagination is needed to read a full population. Ask for more than
nFoundand 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 (classNameas 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 (seetc-capture-awc-calls), not a fourth guess.Query-2014-11-Finder/performSearch- Active Workspace's own search backend, and its documentation is explicit thatmaxToReturn = 0returns "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:StringMap2is a LIST of{key, value}pairs rather than a JSON object, andsearchFilterFieldSortTypeandinternalPropertyNameareuse="required"attributes that are easy to miss. Still unsolved. Same next step.Query-2006-03-SavedQuery/findSavedQueries- 214086 on the body shape tried. UsegetSavedQueriesand 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.