ワンクリックで
aecdm-graphql
Construct correct AECDM GraphQL queries using structured documentation.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Construct correct AECDM GraphQL queries using structured documentation.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | aecdm-graphql |
| description | Construct correct AECDM GraphQL queries using structured documentation. |
📁 instructions/ ← Semantic guidance (HOW to think)
│ 00_start_here.md Core rules, hierarchy, explicit warnings
│ 01_query_entry_points.md Valid entry points only
│ 02_core_object_model.md Object hierarchy and traversal
│ 03_relationships_vs_properties.md Critical: Level ≠ property
│ 04_property_system.md Property types and filtering
│ 05_versioning_and_time.md Version model
│ 06_filters_and_inputs.md Filter input types
│ 07_query_patterns.md Reusable query templates
│
📁 references/ ← Schema reference (WHAT exists)
ref_00 through ref_10 Field names, argument shapes, types
Before constructing ANY AECDM GraphQL query, retrieve:
| Document | Purpose |
|---|---|
instructions/00_start_here.md | Core rules, hierarchy diagram, explicit warnings |
instructions/02_core_object_model.md | Object types and how they connect |
| When the query involves... | Retrieve |
|---|---|
| Level, Space, Room, Host, Type, references, relationships | instructions/03_relationships_vs_properties.md |
| filter, where, search, narrow, Category, property values | instructions/06_filters_and_inputs.md |
property comparison, dimension filter, Length, RSQL, query filter | instructions/06_filters_and_inputs.md |
| version, history, tip, snapshot, time, compare | instructions/05_versioning_and_time.md |
| property, Area, Volume, Mark, metadata, parameters | instructions/04_property_system.md |
| entry point selection, where to start | instructions/01_query_entry_points.md |
Before constructing any new query:
instructions/07_query_patterns.mdref_* Documents For:ref_*) Documents For:Rule of thumb:
"Use
ref_*documents to confirm field names, argument shapes, and valid types. Use non-ref_*documents to decide traversal strategy, filter placement, and relationship semantics."
| Forbidden Action | Why It Fails |
|---|---|
Access element.level as a scalar | Level is a reference to another Element |
Filter by level: "Level 1" at element level | Must filter via references with Level's element ID |
| Treat Level, Space, Room, Type as properties | These are references — relationships to other Elements |
Invent entry points like levels() or model() | Only entry points in 01_query_entry_points.md exist |
Construct filters without checking ref_* docs | Filter fields must exist on the input type |
| Infer relationships from field names alone | Always verify against documentation |
| Assume a level name like "Ground Floor" or "Level 1" exists | Always discover levels first (filter by Category = Levels), then pick by best match |
| Required Action | Example |
|---|---|
| Query Level through references | references(filter: {names: ["Level"]}) { results { value { id name } } } |
| Use validated entry points | elementsByElementGroup, elementGroupAtTip, elementAtTip |
| Check pagination for lists | Include pagination { cursor } and totalCount |
| Verify filter fields exist | Check ElementFilterInput in ref_05_elements.graphql.md |
Use tipVersion for latest data | elementGroupAtTip or versionHistory.tipVersion |
Type referenceId variables as String | referenceId expects String/[String!], not ID — e.g. $levelId: String!, $typeIds: [String!]! |
| Discover levels before filtering by one | Query levels first (Category = Levels), then pick by best name match — never hardcode a level name |
| I want to... | Entry Point | Key Fields |
|---|---|---|
| Get elements from a model | elementsByElementGroup(elementGroupId) | results { id name } |
| Get element's level | elementAtTip(elementId) | references(filter: {names: ["Level"]}) { results { value { name } } } |
| Filter by category | elementsByElementGroup + filter | filter: { properties: [{ name: "Category", value: ["Walls"] }] } |
| Find elements on a level | elementsByElementGroup + filter | filter: { references: [{ name: "Level", referenceId: ["{{levelId}}"] }] } |
| Get model at latest version | elementGroupAtTip(elementGroupId) | Direct access to tip |
| Discover all levels (always do first) | elementsByElementGroup + filter | filter: { properties: [{ name: "Category", value: ["Levels"] }] } |
| Find elements by a Type property (e.g. wide doors) | Two-step: elementsByElementGroup x2 | Step 1: query Types by property → collect Type IDs. Step 2: filter instances by references: [{name: "Type", referenceId: $typeIds}] |
| Document | Contains |
|---|---|
ref_00_schema-core.graphql.md | Scalars (DateTime, PropertyValue), Enums (Comparators), Directives |
ref_01_query-and-subscription.graphql.md | Query and Subscription root types |
ref_02_pagination.graphql.md | Pagination, PaginationInput |
ref_03_hubs-projects-folders.graphql.md | Hub, Project, Folder types and filters |
ref_04_element-groups-and-versions.graphql.md | ElementGroup, ElementGroupVersion, version history |
ref_05_elements.graphql.md | Element type, ElementFilterInput, ElementPropertyFilterInput |
ref_06_properties-values-and-distinct.graphql.md | Property, PropertyValue, distinct value queries |
ref_07_property-definitions.graphql.md | PropertyDefinition type |
ref_08_property-definition-collections.graphql.md | PropertyDefinitionCollection |
ref_09_reference-properties.graphql.md | ReferenceProperty, ReferencePropertyFilterInput |
ref_10_users-and-units.graphql.md | User, Units types |
If you find yourself doing any of these, STOP and re-read the relevant instructional document:
| Warning Sign | Correct Action |
|---|---|
Writing element.level | Use element.references(filter: {names: ["Level"]}) |
Writing filter: { level: "..." } | Use filter: { references: [{ name: "Level", referenceId: [...] }] } |
Looking for Level in properties | Level is a reference, not a property |
Inventing levels() or rooms() entry points | Use elementsByElementGroup with appropriate filters |
| Skipping pagination on list queries | Always check pagination.cursor for more results |
Treating references.value as a string | value is an Element object with id, name, etc. |
Declaring referenceId variables as ID | referenceId expects String — use $levelId: String!, not $levelId: ID! |
| Filtering instances by a property that lives on the Type | Use a two-step query: find matching Type IDs first, then filter instances by Type reference |
Hardcoding a level name like "Ground Floor" or "Level 1" | Discover levels first, then pick by best match — level names vary across models |
When a model is loaded via the render-model tool, the response includes the elementGroupId. This ID is required for nearly all AECDM GraphQL queries. The MCP server also stores this value and can return it via get-model-context.
Important: The user can change the loaded model at any time via the hub browser UI without Claude being directly involved. When this happens, the server-side model context is updated and app.updateModelContext() pushes the new context to Claude. However, to guarantee correctness, Claude must always verify the current model context before constructing queries.
| Rule | Details |
|---|---|
ALWAYS call get-model-context before constructing any query | The user may have changed the model via the browser at any time. Always call get-model-context to get the latest elementGroupId before each query or query chain. Do NOT rely on a previously cached elementGroupId from conversation history. |
Only call browse-aecdm when no model has been loaded | The browser UI is only needed for the initial model selection. If get-model-context returns an error saying no model is loaded, then call browse-aecdm. |
| Do NOT cache the elementGroupId across turns | Each time you need to construct a query, call get-model-context fresh. The model may have changed between turns. |
User asks a query → call get-model-context
→ GOT elementGroupId: construct query + call execute-query
→ NO model loaded: call browse-aecdm to select a model
→ then call get-model-context again
This skill uses MCP (Model Context Protocol) tools to authenticate and execute queries against the AECDM API.
| Tool | Purpose |
|---|---|
authenticate | Authenticate via OAuth PKCE flow and obtain access token |
execute-query | Execute a dynamically constructed GraphQL query with variables |
get-model-context | Returns the currently loaded model's elementGroupId, name, and URN |
render-model | Render a model in the viewer; returns the elementGroupId for subsequent queries |
highlight-elements | Highlight elements in the viewer by their external IDs |
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | The GraphQL query string |
variables | string | No | JSON object of query variables (e.g., {"hubId": "xxx"}) |
region | string | No | API region: US, EMEA, or APAC |
1. Resolve Model Context → ALWAYS call get-model-context to get the latest elementGroupId
2. Plan Query Chain → Determine if single or multi-step query needed
3. Construct Query → Build query following this skill's guidance
4. Execute → Call execute-query with query + variables
5. Handle Result → Process success or handle errors
Step-by-step:
Resolve model context - Before constructing any query, always call get-model-context to get the current elementGroupId:
browse-aecdm to let the user select a model, then call get-model-context again.Plan the query chain - Analyze the user request to determine if it requires:
Construct query - Build the GraphQL query following this skill's documentation:
ref_* docs for exact field namesExecute - Call execute-query MCP tool with:
{
"query": "query GetWalls($elementGroupId: ID!) { elementsByElementGroup(elementGroupId: $elementGroupId, filter: { properties: [{ name: \"Category\", value: [\"Walls\"] }] }) { results { id name } } }",
"variables": "{\"elementGroupId\": \"your-element-group-id\"}",
"region": "US"
}
Handle result:
highlight-elements with external IDs to filter/highlight in the viewerWhen a query fails, follow this error correction loop:
1. Parse Error → Extract error message from response
2. Identify Issue → Determine error type (field, filter, auth, etc.)
3. Consult Docs → Find correct syntax in ref_* documents
4. Fix Query → Apply correction to query
5. Retry → Execute again (max 3 attempts)
| Error Type | Indicator | Resolution |
|---|---|---|
| Field not found | Cannot query field "X" | Check ref_05_elements.graphql.md for valid field names |
| Invalid filter | Unknown argument or filter syntax errors | Check ref_06_filters_and_inputs.graphql.md for filter structure |
| Invalid enum | Enum value not found | Check ref_00_schema-core.graphql.md for valid enum values |
| Missing argument | Argument "X" is required | Add required argument from relevant ref_* doc |
| Auth error (401) | Unauthorized or 401 status | Re-authenticate via authenticate, then retry |
| Rate limited (429) | Too Many Requests | Wait and retry (MCP handles automatically) |
The execute-query tool returns errors in this format:
{
"errors": [
{ "message": "Cannot query field \"invalidField\" on type \"Element\"" }
]
}
Many user requests require multiple queries where results from one query feed into the next.
| Goal | Chain |
|---|---|
| Get elements from the loaded model | Use elementGroupId from model context → elementsByElementGroup |
| Get elements on a specific level | Use elementGroupId → Discover Levels (Category = Levels) → pick best match → Elements (filtered by Level) |
| Get all properties of specific elements | Use elementGroupId → Elements (to get IDs) → Element details with properties |
| Get elements from a different project | Hub → Project → ElementGroup → Elements (only if no model is loaded) |
| Get instances matching a Type property on a Level | Types (filter by property via RSQL query) → collect Type IDs → Instances (filtered by Level + Type references) |
┌─────────────────┐
│ User Request │
└────────┬────────┘
▼
┌─────────────────┐
│ Plan Query Chain│ ← Determine all queries needed
└────────┬────────┘
▼
┌─────────────────┐
│ Execute Query 1 │
└────────┬────────┘
▼
┌─────────────────┐
│ Extract IDs │ ← Pull IDs from results for next query
└────────┬────────┘
▼
┌─────────────────┐
│ Execute Query 2 │ ← Use extracted IDs as variables
└────────┬────────┘
▼
(repeat as needed)
▼
┌─────────────────┐
│ Return Results │
└─────────────────┘
Important: Do not assume a level name like "Level 1" or "Ground Floor" exists. Always discover levels first by querying with
Category=Levels, then pick the best match from the results.
Step 1: Discover levels and find the target level's element ID
query DiscoverLevels($elementGroupId: ID!) {
elementsByElementGroup(elementGroupId: $elementGroupId,
filter: {
properties: [{ name: "Category", value: ["Levels"] }]
}) {
results { id name }
}
}
Variables: {"elementGroupId": "your-element-group-id"}
Pick the level that best matches the user's intent (e.g. "Level 1") from the returned results.
Step 2: Extract the Level ID from results
{
"data": {
"elementsByElementGroup": {
"results": [{ "id": "level-1-element-id", "name": "Level 1" }]
}
}
}
Step 3: Query walls filtered by Level reference
query GetWallsOnLevel($elementGroupId: ID!, $levelId: String!) {
elementsByElementGroup(elementGroupId: $elementGroupId,
filter: {
properties: [{ name: "Category", value: ["Walls"] }],
references: [{ name: "Level", referenceId: [$levelId] }]
}) {
results { id name properties { results { name value } } }
}
}
Variables: {"elementGroupId": "your-element-group-id", "levelId": "level-1-element-id"}
| From Query | Extract | For Next Query |
|---|---|---|
hubs | results[].id | projects(hubId: $hubId) |
projects | results[].id | elementGroupsByProject(projectId: $projectId) |
elementGroupsByProject | results[].id | elementsByElementGroup(elementGroupId: $elementGroupId) |
| Level/Space elements | results[].id | filter: { references: [{ name: "Level", referenceId: [$id] }] } |
| Type elements (matching a property) | results[].id | filter: { references: [{ name: "Type", referenceId: $typeIds }] } |