@yottagraph-app/aether-instructions 1.1.20 → 1.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/rules/api.mdc +111 -77
- package/rules/cookbook.mdc +12 -2
package/package.json
CHANGED
package/rules/api.mdc
CHANGED
|
@@ -28,13 +28,32 @@ For Lovelace **entity types, properties, relationships, and per-source schemas**
|
|
|
28
28
|
|
|
29
29
|
## Test Before You Build
|
|
30
30
|
|
|
31
|
-
**ALWAYS test
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
to server-side code, where you can't inspect responses in the browser console.
|
|
31
|
+
**ALWAYS test data access before writing application code.** The Elemental
|
|
32
|
+
API has response shapes that differ from what the TypeScript types suggest,
|
|
33
|
+
and assumptions about nesting, property formats, and field names will be
|
|
34
|
+
wrong without testing.
|
|
36
35
|
|
|
37
|
-
###
|
|
36
|
+
### Step 1: MCP tools (interactive exploration)
|
|
37
|
+
|
|
38
|
+
**If MCP tools appear in your tool list, start here.** MCP handles entity
|
|
39
|
+
resolution, PID lookups, and NEID formatting automatically — use it to
|
|
40
|
+
verify what data exists and how it's structured.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
elemental_get_schema() → list all entity types
|
|
44
|
+
elemental_get_schema(flavor="article") → properties for a type
|
|
45
|
+
elemental_get_entity(entity="Apple") → resolve + fetch entity
|
|
46
|
+
elemental_get_related(entity="Apple",
|
|
47
|
+
related_flavor="person") → follow relationships
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
MCP tells you the correct flavor IDs, property IDs, and data shapes. Use
|
|
51
|
+
these to inform your REST implementation.
|
|
52
|
+
|
|
53
|
+
### Step 2: curl (verify exact request/response shapes)
|
|
54
|
+
|
|
55
|
+
MCP doesn't cover every REST endpoint (e.g. `/elemental/find` expressions).
|
|
56
|
+
Test those with curl before implementing them in code.
|
|
38
57
|
|
|
39
58
|
The gateway proxy authenticates on your behalf — no Auth0 tokens needed.
|
|
40
59
|
Read `broadchurch.yaml` for the three values you need:
|
|
@@ -48,8 +67,6 @@ Read `broadchurch.yaml` for the three values you need:
|
|
|
48
67
|
Build the request URL as `{gateway.url}/api/qs/{tenant.org_id}/{endpoint}`
|
|
49
68
|
and include the header `X-Api-Key: {gateway.qs_api_key}`.
|
|
50
69
|
|
|
51
|
-
### Example calls
|
|
52
|
-
|
|
53
70
|
```bash
|
|
54
71
|
# Variables — read these from broadchurch.yaml
|
|
55
72
|
GW="https://broadchurch-portal-194773164895.us-central1.run.app"
|
|
@@ -62,6 +79,13 @@ curl -s "$GW/api/qs/$ORG/entities/search" \
|
|
|
62
79
|
-H "X-Api-Key: $KEY" \
|
|
63
80
|
-d '{"queries":[{"queryId":1,"query":"Microsoft"}],"maxResults":3}'
|
|
64
81
|
|
|
82
|
+
# Test a find expression
|
|
83
|
+
curl -s -X POST "$GW/api/qs/$ORG/elemental/find" \
|
|
84
|
+
-H "X-Api-Key: $KEY" \
|
|
85
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
86
|
+
--data-urlencode 'expression={"type":"is_type","is_type":{"fid":12}}' \
|
|
87
|
+
--data-urlencode 'limit=5'
|
|
88
|
+
|
|
65
89
|
# Get entity properties (form-encoded)
|
|
66
90
|
curl -s -X POST "$GW/api/qs/$ORG/elemental/entities/properties" \
|
|
67
91
|
-H "X-Api-Key: $KEY" \
|
|
@@ -74,6 +98,14 @@ curl -s -X POST "$GW/api/qs/$ORG/elemental/entities/properties" \
|
|
|
74
98
|
`application/x-www-form-urlencoded` with JSON-stringified parameter values.
|
|
75
99
|
All other endpoints accept `application/json`.
|
|
76
100
|
|
|
101
|
+
**Interpreting errors:** 400 = expression syntax is wrong. 500 = expression
|
|
102
|
+
is valid but the query failed (wrong PID, unsupported operator for that
|
|
103
|
+
property type). 200 + empty `eids` = query worked but no results match.
|
|
104
|
+
|
|
105
|
+
### Step 3: Implement with confidence
|
|
106
|
+
|
|
107
|
+
Now write your composable or server route, knowing the exact API shapes.
|
|
108
|
+
|
|
77
109
|
## Client Usage
|
|
78
110
|
|
|
79
111
|
All API calls go through `useElementalClient()` from `@yottagraph-app/elemental-api/client`.
|
|
@@ -85,19 +117,12 @@ import { useElementalClient } from '@yottagraph-app/elemental-api/client';
|
|
|
85
117
|
const client = useElementalClient();
|
|
86
118
|
|
|
87
119
|
const schema = await client.getSchema();
|
|
88
|
-
const report = await client.getNamedEntityReport('00508379502570440213');
|
|
89
120
|
const entities = await client.findEntities({
|
|
90
121
|
expression: JSON.stringify({ type: 'comparison', comparison: { operator: 'string_like', pid: 8, value: 'Apple' } }),
|
|
91
122
|
limit: 5,
|
|
92
123
|
});
|
|
93
124
|
```
|
|
94
125
|
|
|
95
|
-
Types are also imported from the client:
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
import type { NamedEntityReport } from '@yottagraph-app/elemental-api/client';
|
|
99
|
-
```
|
|
100
|
-
|
|
101
126
|
### Client Method Quick Reference
|
|
102
127
|
|
|
103
128
|
All methods return data directly and throw on non-2xx responses.
|
|
@@ -107,14 +132,17 @@ All methods return data directly and throw on non-2xx responses.
|
|
|
107
132
|
| Method | Signature | Purpose |
|
|
108
133
|
|---|---|---|
|
|
109
134
|
| `findEntities` | `(body: FindEntitiesBody)` | Expression-based search (see `find.md`) |
|
|
110
|
-
| `getNamedEntityReport` | `(neid: string)` | Entity details (name, aliases, type) |
|
|
111
|
-
| `getEntityDetails` | `(neid: string)` | Alias for entity reports |
|
|
112
135
|
|
|
113
136
|
> **Entity search**: Use `findEntities()` with `string_like` on the name PID
|
|
114
137
|
> for name-based searches, or call `POST /entities/search` directly via
|
|
115
138
|
> `$fetch` for batch name resolution with scored ranking (this endpoint is
|
|
116
139
|
> not wrapped by the generated client).
|
|
117
140
|
|
|
141
|
+
> **Entity name lookup**: To get an entity's display name from its NEID,
|
|
142
|
+
> call `GET /entities/{neid}/name` directly via `$fetch` (not on the
|
|
143
|
+
> generated client). Returns `{"name": "..."}`. For all other entity
|
|
144
|
+
> data, use `getPropertyValues()`.
|
|
145
|
+
|
|
118
146
|
**Properties and schema:**
|
|
119
147
|
|
|
120
148
|
| Method | Signature | Purpose |
|
|
@@ -167,56 +195,54 @@ knowledge of what's in the graph.
|
|
|
167
195
|
|
|
168
196
|
## API Gotchas
|
|
169
197
|
|
|
170
|
-
### `getSchema()` response
|
|
198
|
+
### `getSchema()` response structure differs by endpoint
|
|
199
|
+
|
|
200
|
+
There are two schema endpoints with **different response shapes**:
|
|
201
|
+
|
|
202
|
+
| Endpoint | Flavors at | Flavor ID field | Detail level |
|
|
203
|
+
|----------|-----------|-----------------|--------------|
|
|
204
|
+
| `GET /schema` | top-level (`res.flavors`) | `findex` | Rich (display names, units, domains) |
|
|
205
|
+
| `GET /elemental/metadata/schema` | nested (`res.schema.flavors`) | `fid` | Basic (name + type only) |
|
|
171
206
|
|
|
172
|
-
The
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
causes `Cannot read properties of undefined` every time.
|
|
207
|
+
The TypeScript client's `getSchema()` calls `/elemental/metadata/schema`,
|
|
208
|
+
so the response nests data under `.schema`. The generated types may suggest
|
|
209
|
+
top-level access, but it won't work at runtime.
|
|
176
210
|
|
|
177
211
|
```typescript
|
|
178
|
-
// WRONG — will crash
|
|
212
|
+
// WRONG — will crash (data is nested under .schema):
|
|
179
213
|
const res = await client.getSchema();
|
|
180
214
|
const props = res.properties; // undefined!
|
|
181
215
|
|
|
182
|
-
// CORRECT — always
|
|
216
|
+
// CORRECT — always use fallback to handle both shapes:
|
|
183
217
|
const res = await client.getSchema();
|
|
184
218
|
const properties = res.schema?.properties ?? (res as any).properties ?? [];
|
|
185
219
|
const flavors = res.schema?.flavors ?? (res as any).flavors ?? [];
|
|
186
220
|
```
|
|
187
221
|
|
|
188
|
-
|
|
189
|
-
fixed to match the types. Use this pattern every time.
|
|
222
|
+
### Flavor ID field: `fid` vs `findex`
|
|
190
223
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
Same
|
|
194
|
-
(`name`, `aliases`, `type`) exist at the top level. **They don't.** The
|
|
195
|
-
API wraps them in a `.report` container. The generated client does NOT
|
|
196
|
-
unwrap this automatically.
|
|
224
|
+
The flavor identifier has **different field names** depending on the endpoint:
|
|
225
|
+
`GET /schema` returns `findex`, `/elemental/metadata/schema` returns `fid`.
|
|
226
|
+
Same value, different key. Always use a fallback:
|
|
197
227
|
|
|
198
228
|
```typescript
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const res = await client.getNamedEntityReport(neid);
|
|
205
|
-
const name = res.report?.name ?? (res as any).name ?? neid;
|
|
206
|
-
const aliases = res.report?.aliases ?? (res as any).aliases ?? [];
|
|
207
|
-
const type = res.report?.type ?? (res as any).type;
|
|
229
|
+
const articleFlavor = flavors.find(f => f.name === 'article');
|
|
230
|
+
const articleFid = articleFlavor?.fid ?? articleFlavor?.findex ?? null;
|
|
231
|
+
|
|
232
|
+
// When building a FID lookup map:
|
|
233
|
+
const fidMap = new Map(flavors.map(f => [f.fid ?? f.findex, f.name]));
|
|
208
234
|
```
|
|
209
235
|
|
|
210
|
-
The `
|
|
211
|
-
|
|
236
|
+
The `is_type` expression in `/elemental/find` always uses the `fid` key
|
|
237
|
+
regardless of which schema endpoint provided the value.
|
|
212
238
|
|
|
213
239
|
### Relationship property values need zero-padding to form valid NEIDs
|
|
214
240
|
|
|
215
241
|
Relationship properties (`data_nindex`) return linked entity IDs as raw
|
|
216
242
|
numbers (e.g. `4926132345040704022`). These must be **zero-padded to 20
|
|
217
243
|
characters** to form valid NEIDs. This is easy to miss and causes silent
|
|
218
|
-
failures — `
|
|
219
|
-
returns
|
|
244
|
+
failures — `getPropertyValues` returns empty results and
|
|
245
|
+
`/entities/{neid}/name` returns a 404.
|
|
220
246
|
|
|
221
247
|
```typescript
|
|
222
248
|
// WRONG — raw value is NOT a valid NEID:
|
|
@@ -299,6 +325,23 @@ const docNeids = (res.values ?? []).map((v) => String(v.value).padStart(20, '0')
|
|
|
299
325
|
|
|
300
326
|
See the **cookbook** rule for a full "Get filings for a company" recipe.
|
|
301
327
|
|
|
328
|
+
### Expression language pitfalls
|
|
329
|
+
|
|
330
|
+
These mistakes come up repeatedly when building `/elemental/find` queries:
|
|
331
|
+
|
|
332
|
+
- **Entity type filtering**: Use `is_type` (not `comparison` with pid=0).
|
|
333
|
+
`comparison` requires `pid != 0`.
|
|
334
|
+
- **`string_like` is name-only**: Only works on the name property (PID 8).
|
|
335
|
+
Use `eq` for exact matches on other string properties.
|
|
336
|
+
- **Boolean combinators**: Use `{"type": "and", "and": [...]}` — not
|
|
337
|
+
`conjunction` or any other name.
|
|
338
|
+
- **`lt`/`gt` are numeric-only**: Only work on `data_int` and `data_float`
|
|
339
|
+
properties.
|
|
340
|
+
- **`regex` is not implemented**: Will return an error.
|
|
341
|
+
|
|
342
|
+
Read the "Common Mistakes" section in the **elemental-api skill** (`find.md`)
|
|
343
|
+
for examples of each.
|
|
344
|
+
|
|
302
345
|
### Entity Search
|
|
303
346
|
|
|
304
347
|
Use `client.findEntities()` (`POST /elemental/find`) for entity search.
|
|
@@ -353,17 +396,13 @@ const response = await getArticle(artid);
|
|
|
353
396
|
if (response.status === 404) { /* handle not found */ }
|
|
354
397
|
```
|
|
355
398
|
|
|
356
|
-
## Lovelace MCP Servers
|
|
399
|
+
## Lovelace MCP Servers
|
|
357
400
|
|
|
358
|
-
Four MCP servers
|
|
359
|
-
data exploration.
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
**Before referencing MCP tools, check your available tool list.** If tools
|
|
364
|
-
like `elemental_get_schema` don't appear in your MCP server list, the
|
|
365
|
-
servers aren't connected — just use the REST client and skill docs instead.
|
|
366
|
-
Do not report this as a problem; it's a normal configuration state.
|
|
401
|
+
Four MCP servers may be configured in `.cursor/mcp.json` for interactive
|
|
402
|
+
data exploration. **Check your tool list** — if tools like
|
|
403
|
+
`elemental_get_schema` appear, use them as your primary testing and
|
|
404
|
+
exploration interface before writing code. If they don't appear, the
|
|
405
|
+
servers aren't connected; use curl and the skill docs instead.
|
|
367
406
|
|
|
368
407
|
| Server | What it provides |
|
|
369
408
|
|---|---|
|
|
@@ -372,6 +411,23 @@ Do not report this as a problem; it's a normal configuration state.
|
|
|
372
411
|
| `lovelace-wiki` | Wikipedia entity enrichment |
|
|
373
412
|
| `lovelace-polymarket` | Prediction market data |
|
|
374
413
|
|
|
414
|
+
### MCP Tool Quick Reference
|
|
415
|
+
|
|
416
|
+
| Tool | Purpose | Use to verify... |
|
|
417
|
+
|---|---|---|
|
|
418
|
+
| `elemental_get_schema` | Discover entity types (flavors), properties, and relationships | Flavor IDs, property IDs, data types |
|
|
419
|
+
| `elemental_get_entity` | Look up entity by name or NEID; returns properties | Entity resolution, property shapes |
|
|
420
|
+
| `elemental_get_related` | Related entities with type/relationship filters | Relationship types and traversal |
|
|
421
|
+
| `elemental_get_relationships` | Relationship types and counts between two entities | Edge types between specific entities |
|
|
422
|
+
| `elemental_graph_neighborhood` | Most influential neighbors of an entity | Graph connectivity |
|
|
423
|
+
| `elemental_graph_sentiment` | Sentiment analysis from news articles | Sentiment data availability |
|
|
424
|
+
| `elemental_get_events` | Events for an entity or by search query | Event categories and shapes |
|
|
425
|
+
| `elemental_health` | Health check | Server connectivity |
|
|
426
|
+
|
|
427
|
+
MCP tools handle entity resolution, PID lookups, and NEID formatting
|
|
428
|
+
automatically. Use them to discover IDs and verify data exists before
|
|
429
|
+
building REST-based features with `useElementalClient()`.
|
|
430
|
+
|
|
375
431
|
### Setup
|
|
376
432
|
|
|
377
433
|
`.cursor/mcp.json` is auto-generated by `init-project.js`. If it's missing,
|
|
@@ -379,25 +435,3 @@ run `node init-project.js --local` to regenerate it. For provisioned projects,
|
|
|
379
435
|
the servers route through the Portal Gateway proxy (no credentials needed).
|
|
380
436
|
For local development without a gateway, the servers require an
|
|
381
437
|
`AUTH0_M2M_DEV_TOKEN` environment variable.
|
|
382
|
-
|
|
383
|
-
These servers are also accessible from the browser through the Portal
|
|
384
|
-
Gateway, so you can build MCP tool exploration UIs if needed.
|
|
385
|
-
|
|
386
|
-
### When MCP Servers Are Available
|
|
387
|
-
|
|
388
|
-
If the MCP tools appear in your tool list, you can use them for interactive
|
|
389
|
-
exploration alongside the REST client:
|
|
390
|
-
|
|
391
|
-
| Tool | Purpose |
|
|
392
|
-
|---|---|
|
|
393
|
-
| `elemental_get_schema` | Discover entity types (flavors), properties, and relationships |
|
|
394
|
-
| `elemental_get_entity` | Look up entity by name or NEID; returns properties |
|
|
395
|
-
| `elemental_get_related` | Related entities with type/relationship filters |
|
|
396
|
-
| `elemental_get_relationships` | Relationship types and counts between two entities |
|
|
397
|
-
| `elemental_graph_neighborhood` | Most influential neighbors of an entity |
|
|
398
|
-
| `elemental_graph_sentiment` | Sentiment analysis from news articles |
|
|
399
|
-
| `elemental_get_events` | Events for an entity or by search query |
|
|
400
|
-
| `elemental_health` | Health check |
|
|
401
|
-
|
|
402
|
-
MCP is convenient for schema discovery and entity resolution during planning.
|
|
403
|
-
For building UI features, always use the REST client (`useElementalClient()`).
|
package/rules/cookbook.mdc
CHANGED
|
@@ -501,11 +501,21 @@ relationship PID. See the `api` rule for the two-layer architecture.
|
|
|
501
501
|
String(v.value).padStart(20, '0'),
|
|
502
502
|
);
|
|
503
503
|
|
|
504
|
+
function getEntityNameUrl(neid: string) {
|
|
505
|
+
const config = useRuntimeConfig();
|
|
506
|
+
const gw = (config.public as any).gatewayUrl as string;
|
|
507
|
+
const org = (config.public as any).tenantOrgId as string;
|
|
508
|
+
return `${gw}/api/qs/${org}/entities/${neid}/name`;
|
|
509
|
+
}
|
|
510
|
+
|
|
504
511
|
const names = await Promise.all(
|
|
505
512
|
docNeids.map(async (neid: string) => {
|
|
506
513
|
try {
|
|
507
|
-
const
|
|
508
|
-
|
|
514
|
+
const res = await $fetch<{ name: string }>(
|
|
515
|
+
getEntityNameUrl(neid),
|
|
516
|
+
{ headers: { 'X-Api-Key': getApiKey() } },
|
|
517
|
+
);
|
|
518
|
+
return res.name || neid;
|
|
509
519
|
} catch {
|
|
510
520
|
return neid;
|
|
511
521
|
}
|