@yottagraph-app/aether-instructions 1.1.41 → 1.1.43
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/AGENTS.md +18 -191
- package/README.md +13 -9
- package/commands/build_my_app.md +7 -8
- package/commands/deploy_agent.md +7 -7
- package/commands/update_branding.md +1 -1
- package/commands/update_instructions.md +25 -17
- package/package.json +2 -4
- package/skills/aether/SKILL.md +57 -0
- package/{rules/agents-data.mdc → skills/aether/agents-data.md} +3 -6
- package/{rules/agents.mdc → skills/aether/agents.md} +49 -28
- package/{rules/architecture.mdc → skills/aether/architecture.md} +14 -21
- package/{rules/branding.mdc → skills/aether/branding.md} +7 -12
- package/{rules/cookbook-data.mdc → skills/aether/cookbook-data.md} +13 -28
- package/{rules/cookbook.mdc → skills/aether/cookbook.md} +2 -9
- package/skills/aether/cursor-cloud.md +57 -0
- package/{rules/data.mdc → skills/aether/data.md} +80 -70
- package/skills/aether/deployment.md +14 -0
- package/{rules/design.mdc → skills/aether/design.md} +1 -6
- package/skills/aether/env.md +10 -0
- package/{rules/git-support.mdc → skills/aether/git-support.md} +4 -9
- package/{rules/instructions_warning.mdc → skills/aether/instructions_warning.md} +6 -12
- package/skills/aether/local-setup.md +15 -0
- package/{rules/mcp-servers.mdc → skills/aether/mcp-servers.md} +3 -7
- package/{rules/pref.mdc → skills/aether/pref.md} +9 -14
- package/skills/aether/server-data.md +48 -0
- package/skills/aether/server.md +60 -0
- package/{rules/something-broke.mdc → skills/aether/something-broke.md} +3 -7
- package/{rules/server.mdc → skills/aether/storage.md} +78 -108
- package/{rules/ui.mdc → skills/aether/ui.md} +2 -6
- package/skills/elemental-mcp-patterns/SKILL.md +57 -51
- package/variants/mcp-only/commands/build_my_app.md +6 -6
- package/variants/mcp-only/{rules/agents-data.mdc → skills/aether/agents-data.md} +0 -6
- package/variants/mcp-only/{rules/cookbook-data.mdc → skills/aether/cookbook-data.md} +3 -6
- package/variants/mcp-only/{rules/data.mdc → skills/aether/data.md} +1 -6
- package/variants/mcp-only/{rules/server-data.mdc → skills/aether/server-data.md} +9 -15
- package/rules/aether.mdc +0 -21
- package/rules/server-data.mdc +0 -54
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Data access via Elemental API: client usage, schema discovery, entity search, API gotchas, MCP servers. Read when building features that fetch or display data from the Query Server."
|
|
3
|
-
alwaysApply: false
|
|
4
|
-
---
|
|
5
1
|
# Data — Elemental API (platform data source)
|
|
6
2
|
|
|
7
3
|
**This app is built on the Lovelace platform.** The Query Server is the
|
|
@@ -81,10 +77,10 @@ Test those with curl before implementing them in code.
|
|
|
81
77
|
The gateway proxy authenticates on your behalf — no Auth0 tokens needed.
|
|
82
78
|
Read `broadchurch.yaml` for the three values you need:
|
|
83
79
|
|
|
84
|
-
| YAML path
|
|
85
|
-
|
|
86
|
-
| `gateway.url`
|
|
87
|
-
| `tenant.org_id`
|
|
80
|
+
| YAML path | Purpose |
|
|
81
|
+
| -------------------- | ------------------------------------ |
|
|
82
|
+
| `gateway.url` | Portal Gateway base URL |
|
|
83
|
+
| `tenant.org_id` | Your tenant ID (path segment) |
|
|
88
84
|
| `gateway.qs_api_key` | API key (sent as `X-Api-Key` header) |
|
|
89
85
|
|
|
90
86
|
Build the request URL as `{gateway.url}/api/qs/{tenant.org_id}/{endpoint}`
|
|
@@ -141,9 +137,9 @@ Elemental API patterns. **Use these instead of writing from scratch:**
|
|
|
141
137
|
|
|
142
138
|
```typescript
|
|
143
139
|
const { flavors, properties, flavorByName, pidByName, refresh } = useElementalSchema();
|
|
144
|
-
await refresh();
|
|
145
|
-
const articleFid = flavorByName('article');
|
|
146
|
-
const namePid = pidByName('name');
|
|
140
|
+
await refresh(); // fetches once, then cached
|
|
141
|
+
const articleFid = flavorByName('article'); // → string | null
|
|
142
|
+
const namePid = pidByName('name'); // → string | null
|
|
147
143
|
```
|
|
148
144
|
|
|
149
145
|
Handles the dual response shapes (`res.schema.flavors` vs `res.flavors`)
|
|
@@ -152,14 +148,20 @@ and the `fid`/`findex` naming inconsistency automatically.
|
|
|
152
148
|
### `utils/elementalHelpers` — Gateway URL Helpers
|
|
153
149
|
|
|
154
150
|
```typescript
|
|
155
|
-
import {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
151
|
+
import {
|
|
152
|
+
buildGatewayUrl,
|
|
153
|
+
getApiKey,
|
|
154
|
+
padNeid,
|
|
155
|
+
searchEntities,
|
|
156
|
+
getEntityName,
|
|
157
|
+
} from '~/utils/elementalHelpers';
|
|
158
|
+
|
|
159
|
+
const url = buildGatewayUrl('entities/search'); // full gateway URL
|
|
160
|
+
const key = getApiKey(); // from runtimeConfig
|
|
161
|
+
const neid = padNeid('4926132345040704022'); // → "04926132345040704022"
|
|
160
162
|
|
|
161
163
|
const results = await searchEntities('Microsoft'); // batch name search
|
|
162
|
-
const name = await getEntityName(neid);
|
|
164
|
+
const name = await getEntityName(neid); // display name lookup
|
|
163
165
|
```
|
|
164
166
|
|
|
165
167
|
## Client Usage
|
|
@@ -174,7 +176,10 @@ const client = useElementalClient();
|
|
|
174
176
|
|
|
175
177
|
const schema = await client.getSchema();
|
|
176
178
|
const entities = await client.findEntities({
|
|
177
|
-
expression: JSON.stringify({
|
|
179
|
+
expression: JSON.stringify({
|
|
180
|
+
type: 'comparison',
|
|
181
|
+
comparison: { operator: 'string_like', pid: 8, value: 'Apple' },
|
|
182
|
+
}),
|
|
178
183
|
limit: 5,
|
|
179
184
|
});
|
|
180
185
|
```
|
|
@@ -185,8 +190,8 @@ All methods return data directly and throw on non-2xx responses.
|
|
|
185
190
|
|
|
186
191
|
**Entity search and lookup:**
|
|
187
192
|
|
|
188
|
-
| Method
|
|
189
|
-
|
|
193
|
+
| Method | Signature | Purpose |
|
|
194
|
+
| -------------- | -------------------------- | --------------------------------------- |
|
|
190
195
|
| `findEntities` | `(body: FindEntitiesBody)` | Expression-based search (see `find.md`) |
|
|
191
196
|
|
|
192
197
|
> **Entity search**: Use `findEntities()` with `string_like` on the name PID
|
|
@@ -201,25 +206,25 @@ All methods return data directly and throw on non-2xx responses.
|
|
|
201
206
|
|
|
202
207
|
**Properties and schema:**
|
|
203
208
|
|
|
204
|
-
| Method
|
|
205
|
-
|
|
206
|
-
| `getSchema`
|
|
209
|
+
| Method | Signature | Purpose |
|
|
210
|
+
| ------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
211
|
+
| `getSchema` | `()` | All entity types (flavors) and properties (PIDs) |
|
|
207
212
|
| `getPropertyValues` | `(body: { eids: string, pids: string })` | Property values (eids: JSON array of NEID strings; pids: JSON array of numeric PIDs) |
|
|
208
|
-
| `summarizeProperty` | `(pid: number)`
|
|
213
|
+
| `summarizeProperty` | `(pid: number)` | Summary stats for a property |
|
|
209
214
|
|
|
210
215
|
**Relationships and graph:**
|
|
211
216
|
|
|
212
|
-
| Method
|
|
213
|
-
|
|
217
|
+
| Method | Signature | Purpose |
|
|
218
|
+
| -------------- | -------------------------------- | ------------------------------------------------------------ |
|
|
214
219
|
| `findEntities` | `(body: { expression, limit? })` | Find linked entities via `linked` expression (see `find.md`) |
|
|
215
220
|
|
|
216
221
|
**Other:**
|
|
217
222
|
|
|
218
|
-
| Method
|
|
219
|
-
|
|
220
|
-
| `getHealth`
|
|
221
|
-
| `getStatus`
|
|
222
|
-
| `adaMessage` | `(body: AdaMessageBody)` | Ada AI chat
|
|
223
|
+
| Method | Signature | Purpose |
|
|
224
|
+
| ------------ | ------------------------ | ------------------------------ |
|
|
225
|
+
| `getHealth` | `()` | Health check |
|
|
226
|
+
| `getStatus` | `()` | Server status and capabilities |
|
|
227
|
+
| `adaMessage` | `(body: AdaMessageBody)` | Ada AI chat |
|
|
223
228
|
|
|
224
229
|
## Discovery-First Pattern
|
|
225
230
|
|
|
@@ -230,16 +235,16 @@ types or property names. Instead, discover them at runtime:
|
|
|
230
235
|
1. **Get the schema** — `client.getSchema()` returns all entity types (flavors)
|
|
231
236
|
and properties (PIDs) available in the system. See `schema.md`.
|
|
232
237
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
238
|
+
The schema response contains:
|
|
239
|
+
- **Flavors** (entity types): Company, Person, GovernmentOrg, etc.
|
|
240
|
+
Each flavor has a numeric ID and a human-readable name.
|
|
241
|
+
- **PIDs** (properties): name, country, industry, lei_code, etc.
|
|
242
|
+
Each PID has a type (`data_str`, `data_int`, `data_nindex`, etc.).
|
|
243
|
+
- Properties with type `data_nindex` are references to other entities —
|
|
244
|
+
resolve them with another `getPropertyValues` call.
|
|
240
245
|
|
|
241
|
-
|
|
242
|
-
|
|
246
|
+
Use flavor names in `findEntities()` expressions and PID names in
|
|
247
|
+
`getPropertyValues()`.
|
|
243
248
|
|
|
244
249
|
2. **Search with expressions** — `client.findEntities()` uses a JSON expression
|
|
245
250
|
language to search by type, property value, or relationship. See `find.md`.
|
|
@@ -269,10 +274,10 @@ For reliable agent behavior, prefer typed semantics over string heuristics:
|
|
|
269
274
|
|
|
270
275
|
There are two schema endpoints with **different response shapes**:
|
|
271
276
|
|
|
272
|
-
| Endpoint
|
|
273
|
-
|
|
274
|
-
| `GET /schema`
|
|
275
|
-
| `GET /elemental/metadata/schema` | nested (`res.schema.flavors`) | `fid`
|
|
277
|
+
| Endpoint | Flavors at | Flavor ID field | Detail level |
|
|
278
|
+
| -------------------------------- | ----------------------------- | --------------- | ------------------------------------ |
|
|
279
|
+
| `GET /schema` | top-level (`res.flavors`) | `findex` | Rich (display names, units, domains) |
|
|
280
|
+
| `GET /elemental/metadata/schema` | nested (`res.schema.flavors`) | `fid` | Basic (name + type only) |
|
|
276
281
|
|
|
277
282
|
The TypeScript client's `getSchema()` calls `/elemental/metadata/schema`,
|
|
278
283
|
so the response nests data under `.schema`. The generated types may suggest
|
|
@@ -296,12 +301,12 @@ The flavor identifier has **different field names** depending on the endpoint:
|
|
|
296
301
|
Same value, different key. Always use a fallback:
|
|
297
302
|
|
|
298
303
|
```typescript
|
|
299
|
-
const articleFlavor = flavors.find(f => f.name === 'article');
|
|
304
|
+
const articleFlavor = flavors.find((f) => f.name === 'article');
|
|
300
305
|
// Always use String() — safe for small IDs (12) and required for large ones
|
|
301
306
|
const articleFid = String(articleFlavor?.fid ?? articleFlavor?.findex ?? '');
|
|
302
307
|
|
|
303
308
|
// When building a FID lookup map:
|
|
304
|
-
const fidMap = new Map(flavors.map(f => [String(f.fid ?? f.findex), f.name]));
|
|
309
|
+
const fidMap = new Map(flavors.map((f) => [String(f.fid ?? f.findex), f.name]));
|
|
305
310
|
```
|
|
306
311
|
|
|
307
312
|
The `is_type` expression in `/elemental/find` always uses the `fid` key
|
|
@@ -352,14 +357,14 @@ const filingNeid = String(res.values[0].value).padStart(20, '0'); // "0492613234
|
|
|
352
357
|
```typescript
|
|
353
358
|
// WRONG — PIDs are numbers, not strings:
|
|
354
359
|
const values = await client.getPropertyValues({
|
|
355
|
-
|
|
356
|
-
|
|
360
|
+
eids: JSON.stringify(['00416400910670863867']),
|
|
361
|
+
pids: JSON.stringify(['name', 'country', 'industry']), // FAILS
|
|
357
362
|
});
|
|
358
363
|
|
|
359
364
|
// CORRECT — use numeric PIDs from getSchema():
|
|
360
365
|
const values = await client.getPropertyValues({
|
|
361
|
-
|
|
362
|
-
|
|
366
|
+
eids: JSON.stringify(['00416400910670863867']),
|
|
367
|
+
pids: JSON.stringify([8, 313]), // 8=name, 313=country (from schema)
|
|
363
368
|
});
|
|
364
369
|
```
|
|
365
370
|
|
|
@@ -409,7 +414,7 @@ const res = await client.getPropertyValues({
|
|
|
409
414
|
const docNeids = (res.values ?? []).map((v) => String(v.value).padStart(20, '0'));
|
|
410
415
|
```
|
|
411
416
|
|
|
412
|
-
See
|
|
417
|
+
See [cookbook-data.md](cookbook-data.md) in this skill for a full "Get filings for a company" recipe.
|
|
413
418
|
|
|
414
419
|
### Expression language pitfalls
|
|
415
420
|
|
|
@@ -455,14 +460,17 @@ source-specific schemas.
|
|
|
455
460
|
`getPropertyValues()` with the relationship PID. Values are entity IDs
|
|
456
461
|
that must be zero-padded to 20 characters.
|
|
457
462
|
|
|
458
|
-
See
|
|
463
|
+
See [cookbook-data.md](cookbook-data.md) (news feed recipe) for a full example.
|
|
459
464
|
|
|
460
465
|
## Error Handling
|
|
461
466
|
|
|
462
467
|
```typescript
|
|
463
468
|
try {
|
|
464
469
|
const data = await client.findEntities({
|
|
465
|
-
expression: JSON.stringify({
|
|
470
|
+
expression: JSON.stringify({
|
|
471
|
+
type: 'comparison',
|
|
472
|
+
comparison: { operator: 'string_like', pid: 8, value: 'Apple' },
|
|
473
|
+
}),
|
|
466
474
|
limit: 5,
|
|
467
475
|
});
|
|
468
476
|
} catch (error) {
|
|
@@ -479,7 +487,9 @@ functions instead:
|
|
|
479
487
|
import { getArticle } from '@yottagraph-app/elemental-api/client';
|
|
480
488
|
|
|
481
489
|
const response = await getArticle(artid);
|
|
482
|
-
if (response.status === 404) {
|
|
490
|
+
if (response.status === 404) {
|
|
491
|
+
/* handle not found */
|
|
492
|
+
}
|
|
483
493
|
```
|
|
484
494
|
|
|
485
495
|
## Lovelace MCP Servers
|
|
@@ -490,25 +500,25 @@ data exploration. **Check your tool list** — if tools like
|
|
|
490
500
|
exploration interface before writing code. If they don't appear, the
|
|
491
501
|
servers aren't connected; use curl and the skill docs instead.
|
|
492
502
|
|
|
493
|
-
| Server
|
|
494
|
-
|
|
495
|
-
| `lovelace-elemental`
|
|
496
|
-
| `lovelace-stocks`
|
|
497
|
-
| `lovelace-wiki`
|
|
498
|
-
| `lovelace-polymarket` | Prediction market data
|
|
503
|
+
| Server | What it provides |
|
|
504
|
+
| --------------------- | ----------------------------------------------------------------------------- |
|
|
505
|
+
| `lovelace-elemental` | Knowledge Graph: entities, relationships, events, sentiment, schema discovery |
|
|
506
|
+
| `lovelace-stocks` | Stock/financial market data |
|
|
507
|
+
| `lovelace-wiki` | Wikipedia entity enrichment |
|
|
508
|
+
| `lovelace-polymarket` | Prediction market data |
|
|
499
509
|
|
|
500
510
|
### MCP Tool Quick Reference
|
|
501
511
|
|
|
502
|
-
| Tool
|
|
503
|
-
|
|
504
|
-
| `elemental_get_schema`
|
|
505
|
-
| `elemental_get_entity`
|
|
506
|
-
| `elemental_get_related`
|
|
507
|
-
| `elemental_get_relationships`
|
|
508
|
-
| `elemental_graph_neighborhood` | Most influential neighbors of an entity
|
|
509
|
-
| `elemental_graph_sentiment`
|
|
510
|
-
| `elemental_get_events`
|
|
511
|
-
| `elemental_health`
|
|
512
|
+
| Tool | Purpose | Use to verify... |
|
|
513
|
+
| ------------------------------ | -------------------------------------------------------------- | ------------------------------------ |
|
|
514
|
+
| `elemental_get_schema` | Discover entity types (flavors), properties, and relationships | Flavor IDs, property IDs, data types |
|
|
515
|
+
| `elemental_get_entity` | Look up entity by name or NEID; returns properties | Entity resolution, property shapes |
|
|
516
|
+
| `elemental_get_related` | Related entities with type/relationship filters | Relationship types and traversal |
|
|
517
|
+
| `elemental_get_relationships` | Relationship types and counts between two entities | Edge types between specific entities |
|
|
518
|
+
| `elemental_graph_neighborhood` | Most influential neighbors of an entity | Graph connectivity |
|
|
519
|
+
| `elemental_graph_sentiment` | Sentiment analysis from news articles | Sentiment data availability |
|
|
520
|
+
| `elemental_get_events` | Events for an entity or by search query | Event categories and shapes |
|
|
521
|
+
| `elemental_health` | Health check | Server connectivity |
|
|
512
522
|
|
|
513
523
|
MCP tools handle entity resolution, PID lookups, and NEID formatting
|
|
514
524
|
automatically. Use them to discover IDs and verify data exists before
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
### App (Nuxt UI + server routes)
|
|
2
|
+
|
|
3
|
+
Vercel auto-deploys on every push to `main`. Preview deployments are created for
|
|
4
|
+
other branches. The app is available at `{slug}.yottagraph.app`.
|
|
5
|
+
|
|
6
|
+
### Agents (`agents/`)
|
|
7
|
+
|
|
8
|
+
Each subdirectory in `agents/` is a self-contained Python ADK agent. Deploy via
|
|
9
|
+
the Portal UI or `/deploy_agent` in Cursor.
|
|
10
|
+
|
|
11
|
+
### MCP Servers (`mcp-servers/`)
|
|
12
|
+
|
|
13
|
+
Each subdirectory in `mcp-servers/` is a Python FastMCP server. Deploy via
|
|
14
|
+
the Portal UI or `/deploy_mcp` in Cursor.
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "DESIGN.md workflow, feature docs, starter app is placeholder. Read when starting work, planning features, or updating project design."
|
|
3
|
-
alwaysApply: false
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# Design
|
|
7
2
|
|
|
8
3
|
Read `DESIGN.md` before starting any work. It is the source of truth for project vision, architecture, and current implementation status.
|
|
@@ -58,4 +53,4 @@ If the user is implementing a change that has no feature doc, encourage them to
|
|
|
58
53
|
|
|
59
54
|
**Exception for initial app builds:** When building a new app from scratch via `/build_my_app`, skip per-page feature docs. They add overhead during greenfield builds where the entire app is being created at once. Start using feature docs once the initial app is built and you're iterating on individual features.
|
|
60
55
|
|
|
61
|
-
It is a good practice to close out one feature doc and start a new one as the focus of the work shifts. It is acceptable to be working with multiple feature docs at once, if the user is working on multiple unconnected or loosely connected features. The sections of the feature doc are flexible and you should add or remove sections to fit the needs of the project.
|
|
56
|
+
It is a good practice to close out one feature doc and start a new one as the focus of the work shifts. It is acceptable to be working with multiple feature docs at once, if the user is working on multiple unconnected or loosely connected features. The sections of the feature doc are flexible and you should add or remove sections to fit the needs of the project.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
| Variable | Purpose | Default |
|
|
2
|
+
| ---------------------------------- | -------------------------------- | --------------------------------------- |
|
|
3
|
+
| `NUXT_PUBLIC_APP_ID` | Unique app identifier | derived from directory name |
|
|
4
|
+
| `NUXT_PUBLIC_APP_NAME` | Display name | derived from directory name |
|
|
5
|
+
| `NUXT_PUBLIC_USER_NAME` | Set to any value to bypass Auth0 | `dev-user` in local mode |
|
|
6
|
+
| `NUXT_PUBLIC_QUERY_SERVER_ADDRESS` | Query Server URL | read from `broadchurch.yaml` if present |
|
|
7
|
+
| `NUXT_PUBLIC_GATEWAY_URL` | Portal Gateway for agent chat | read from `broadchurch.yaml` if present |
|
|
8
|
+
| `NUXT_PUBLIC_TENANT_ORG_ID` | Auth0 org ID for this tenant | read from `broadchurch.yaml` if present |
|
|
9
|
+
|
|
10
|
+
See `.env.example` for the full list.
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Git commit workflow and conventions. Apply when finishing implementation work, making commits, or troubleshooting git/pre-commit failures."
|
|
3
|
-
alwaysApply: false
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# Git Workflow
|
|
7
2
|
|
|
8
|
-
This
|
|
3
|
+
This guidance applies both when an agent is committing its own work and when helping a user commit theirs.
|
|
9
4
|
|
|
10
5
|
## Committing
|
|
11
6
|
|
|
@@ -14,9 +9,9 @@ Commit when you finish a meaningful unit of work — a feature, a fix, a refacto
|
|
|
14
9
|
### Steps
|
|
15
10
|
|
|
16
11
|
1. **Run formatting first** — this is required or the commit will fail:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
```bash
|
|
13
|
+
npm run format
|
|
14
|
+
```
|
|
20
15
|
2. **Stage all files** — always use `git add -A`.
|
|
21
16
|
3. **Commit** with the message format below.
|
|
22
17
|
4. **Verify the commit succeeded** with `git status`. If it failed, see Pre-commit Failure Troubleshooting below. Do not proceed until the commit is confirmed.
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Warns users not to modify package-managed instruction files in .cursor/
|
|
3
|
-
alwaysApply: false
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# Aether Instructions Warning
|
|
7
2
|
|
|
8
3
|
**You are editing a file managed by the `@yottagraph-app/aether-instructions` package.**
|
|
@@ -10,9 +5,8 @@ alwaysApply: false
|
|
|
10
5
|
Package-managed files are tracked by `.cursor/.aether-instructions-manifest`.
|
|
11
6
|
They will be **overwritten** when you run `/update_instructions`.
|
|
12
7
|
|
|
13
|
-
This includes files under `.cursor/
|
|
14
|
-
|
|
15
|
-
in the manifest).
|
|
8
|
+
This includes files under `.cursor/commands/`, `.cursor/skills/`, **and the
|
|
9
|
+
root `AGENTS.md`** (tracked as `root/AGENTS.md` in the manifest).
|
|
16
10
|
|
|
17
11
|
## Do Not
|
|
18
12
|
|
|
@@ -20,7 +14,7 @@ in the manifest).
|
|
|
20
14
|
|
|
21
15
|
## To Customize
|
|
22
16
|
|
|
23
|
-
If you need to modify a package-provided
|
|
17
|
+
If you need to modify a package-provided command, skill topic, or the root `AGENTS.md`:
|
|
24
18
|
|
|
25
19
|
1. **Copy** the file to a new name
|
|
26
20
|
2. Make your changes to the copy
|
|
@@ -30,8 +24,8 @@ If you need to modify a package-provided rule, command, or the root `AGENTS.md`:
|
|
|
30
24
|
Examples:
|
|
31
25
|
|
|
32
26
|
```bash
|
|
33
|
-
# Customize a
|
|
34
|
-
cp .cursor/
|
|
27
|
+
# Customize a skill topic
|
|
28
|
+
cp .cursor/skills/aether/data.md .cursor/skills/aether/data_custom.md
|
|
35
29
|
|
|
36
30
|
# Customize AGENTS.md
|
|
37
31
|
cp AGENTS.md AGENTS.local.md
|
|
@@ -44,7 +38,7 @@ cp AGENTS.md AGENTS.local.md
|
|
|
44
38
|
## How It Works
|
|
45
39
|
|
|
46
40
|
- `.cursor/.aether-instructions-manifest` lists every file installed by the
|
|
47
|
-
package (one relative path per line, e.g. `
|
|
41
|
+
package (one relative path per line, e.g. `skills/aether/data.md`). Entries
|
|
48
42
|
prefixed with `root/` refer to files at the tenant repo root
|
|
49
43
|
(currently only `root/AGENTS.md`).
|
|
50
44
|
- `/update_instructions` deletes manifest entries, extracts fresh files from
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## Manual / Local Setup
|
|
2
|
+
|
|
3
|
+
Node 20 is the baseline (pinned in `.nvmrc`). Newer versions generally work.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm run init -- --local # creates .env with dev defaults (no Auth0)
|
|
7
|
+
npm install # all deps are public on npmjs.com -- no tokens needed
|
|
8
|
+
npm run dev # dev server on port 3000
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
For the full interactive wizard (project name, Auth0, query server, etc.):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm run init # interactive, or --non-interactive for CI (see --help)
|
|
15
|
+
```
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Rules for developing MCP servers in the mcp-servers/ directory
|
|
3
|
-
globs: mcp-servers/**
|
|
4
|
-
alwaysApply: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
1
|
# MCP Server Development (FastMCP)
|
|
8
2
|
|
|
9
3
|
This project supports developing and deploying custom MCP (Model Context Protocol) servers alongside the UI. Servers live in the `mcp-servers/` directory and deploy to Google Cloud Run via the `/deploy_mcp` command.
|
|
@@ -63,6 +57,7 @@ if __name__ == "__main__":
|
|
|
63
57
|
```
|
|
64
58
|
|
|
65
59
|
Key rules:
|
|
60
|
+
|
|
66
61
|
- The FastMCP instance MUST be named `mcp` — the Dockerfile runs `fastmcp run server:mcp` to find it
|
|
67
62
|
- FastMCP 2.x takes a single positional name argument: `FastMCP("name")`. Do NOT pass `name=` or `description=` as keyword arguments
|
|
68
63
|
- Use `@mcp.tool()` decorators to define tools
|
|
@@ -88,6 +83,7 @@ python -m fastmcp run server:mcp --transport streamable-http --host 0.0.0.0 --po
|
|
|
88
83
|
## Deployment
|
|
89
84
|
|
|
90
85
|
Deploy with the `/deploy_mcp` Cursor command. This will:
|
|
86
|
+
|
|
91
87
|
1. Build a container image via Cloud Build
|
|
92
88
|
2. Deploy to Cloud Run with IAM authentication
|
|
93
89
|
3. Grant invoker permissions to tenant and Portal service accounts
|
|
@@ -102,7 +98,7 @@ Once deployed, agents can connect to your MCP server. Add the Cloud Run URL to y
|
|
|
102
98
|
|
|
103
99
|
- One server per data domain or external service
|
|
104
100
|
- Keep tools focused and well-documented
|
|
105
|
-
- Return structured data (dicts/lists), not raw strings — MCP handles serialization via the protocol. (This differs from ADK agent tools, which should return formatted strings because the LLM reads tool output directly — see
|
|
101
|
+
- Return structured data (dicts/lists), not raw strings — MCP handles serialization via the protocol. (This differs from ADK agent tools, which should return formatted strings because the LLM reads tool output directly — see [agents.md](agents.md) in this skill.)
|
|
106
102
|
- Handle errors by returning descriptive error messages in the response
|
|
107
103
|
- Use environment variables for API keys and configuration (never hardcode secrets)
|
|
108
104
|
- For secrets, use GCP Secret Manager and access at runtime
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Apply when working with user preferences, settings persistence, usePrefsStore, the Pref class, KV storage, or app namespacing (NUXT_PUBLIC_APP_ID).
|
|
3
|
-
alwaysApply: false
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# User Preferences
|
|
7
2
|
|
|
8
3
|
Preferences are persisted to Upstash Redis (KV) via `usePrefsStore()` from
|
|
@@ -28,7 +23,7 @@ const myPref = new Pref<string>(docPath, 'fieldName', 'defaultValue');
|
|
|
28
23
|
await myPref.initialize();
|
|
29
24
|
|
|
30
25
|
myPref.r.value; // reactive ref (use in templates)
|
|
31
|
-
myPref.v;
|
|
26
|
+
myPref.v; // getter shorthand
|
|
32
27
|
myPref.set('new value'); // persists to KV
|
|
33
28
|
```
|
|
34
29
|
|
|
@@ -79,13 +74,13 @@ routes directly from client-side code:
|
|
|
79
74
|
```typescript
|
|
80
75
|
// Read
|
|
81
76
|
const value = await $fetch('/api/kv/read', {
|
|
82
|
-
|
|
77
|
+
params: { docPath: '/users/abc/settings', fieldName: 'theme' },
|
|
83
78
|
});
|
|
84
79
|
|
|
85
80
|
// Write
|
|
86
81
|
await $fetch('/api/kv/write', {
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
method: 'POST',
|
|
83
|
+
body: { docPath: '/users/abc/settings', fieldName: 'theme', value: '"dark"' },
|
|
89
84
|
});
|
|
90
85
|
```
|
|
91
86
|
|
|
@@ -121,8 +116,8 @@ function useMyFeaturePrefs() {
|
|
|
121
116
|
|
|
122
117
|
## Scope Guidance
|
|
123
118
|
|
|
124
|
-
| App-specific
|
|
125
|
-
|
|
126
|
-
| Layout prefs, favorites
|
|
127
|
-
| Watchlists, feature settings | Accessibility
|
|
128
|
-
| Feature-specific settings
|
|
119
|
+
| App-specific | Global |
|
|
120
|
+
| ---------------------------- | ----------------------- |
|
|
121
|
+
| Layout prefs, favorites | Language |
|
|
122
|
+
| Watchlists, feature settings | Accessibility |
|
|
123
|
+
| Feature-specific settings | Timezone, notifications |
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Server routes: Elemental API (Query Server)
|
|
2
|
+
|
|
3
|
+
Server routes can call the Elemental API through the Portal Gateway proxy,
|
|
4
|
+
just like client-side code does. The gateway URL, tenant org ID, and API key
|
|
5
|
+
are available via `useRuntimeConfig()`.
|
|
6
|
+
|
|
7
|
+
**NEVER use `readFileSync('broadchurch.yaml')` in server routes.** The YAML
|
|
8
|
+
file is read at build time by `nuxt.config.ts` and its values flow into
|
|
9
|
+
`runtimeConfig`. Nitro serverless functions (Vercel) don't bundle arbitrary
|
|
10
|
+
project files — `readFileSync` will crash with ENOENT in production even
|
|
11
|
+
though it works locally.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
export default defineEventHandler(async (event) => {
|
|
15
|
+
const { public: config } = useRuntimeConfig();
|
|
16
|
+
|
|
17
|
+
const gatewayUrl = config.gatewayUrl; // Portal Gateway base URL
|
|
18
|
+
const orgId = config.tenantOrgId; // Tenant org ID (path segment)
|
|
19
|
+
const apiKey = config.qsApiKey; // API key for X-Api-Key header
|
|
20
|
+
|
|
21
|
+
if (!gatewayUrl || !orgId) {
|
|
22
|
+
throw createError({ statusCode: 503, statusMessage: 'Gateway not configured' });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const res = await $fetch(`${gatewayUrl}/api/qs/${orgId}/entities/search`, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: {
|
|
28
|
+
'Content-Type': 'application/json',
|
|
29
|
+
...(apiKey && { 'X-Api-Key': apiKey }),
|
|
30
|
+
},
|
|
31
|
+
body: { queries: [{ queryId: 1, query: 'Microsoft' }], maxResults: 5 },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return res;
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Available runtime config keys (all under `runtimeConfig.public`):
|
|
39
|
+
|
|
40
|
+
| Key | Source | Purpose |
|
|
41
|
+
| -------------------- | ----------------------------------------- | ------------------------------ |
|
|
42
|
+
| `gatewayUrl` | `broadchurch.yaml` → `gateway.url` | Portal Gateway base URL |
|
|
43
|
+
| `tenantOrgId` | `broadchurch.yaml` → `tenant.org_id` | Tenant ID for API path |
|
|
44
|
+
| `qsApiKey` | `broadchurch.yaml` → `gateway.qs_api_key` | API key sent as `X-Api-Key` |
|
|
45
|
+
| `queryServerAddress` | `broadchurch.yaml` → `query_server.url` | Direct QS URL (prefer gateway) |
|
|
46
|
+
|
|
47
|
+
Build the request URL as `{gatewayUrl}/api/qs/{tenantOrgId}/{endpoint}`.
|
|
48
|
+
See [data.md](data.md) in this skill for endpoint reference and response shapes.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Nitro Server Routes
|
|
2
|
+
|
|
3
|
+
The `server/` directory contains Nuxt's Nitro server layer. These routes deploy
|
|
4
|
+
with the app to Vercel -- they are NOT a separate service. They handle
|
|
5
|
+
server-side concerns like KV storage, database access, and image proxying
|
|
6
|
+
that can't run in the browser.
|
|
7
|
+
|
|
8
|
+
## Directory Layout
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
server/
|
|
12
|
+
├── api/
|
|
13
|
+
│ ├── kv/ # KV (Upstash Redis) CRUD — read, write, delete, documents, status
|
|
14
|
+
│ └── avatar/[url].ts # Avatar image proxy
|
|
15
|
+
└── utils/
|
|
16
|
+
├── redis.ts # Upstash Redis client (lazy-init from KV_REST_API_URL)
|
|
17
|
+
├── neon.ts # Neon Postgres client (lazy-init from DATABASE_URL) — create if missing when Neon is provisioned
|
|
18
|
+
└── cookies.ts # Cookie handling (@hapi/iron)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For KV and Neon Postgres access (client usage, provisioning checks, creating
|
|
22
|
+
tables, handling missing credentials gracefully), see
|
|
23
|
+
[storage.md](storage.md) in this skill. For calling the platform Query
|
|
24
|
+
Server from Nitro routes, see [server-data.md](server-data.md) in this
|
|
25
|
+
skill.
|
|
26
|
+
|
|
27
|
+
## Adding Routes
|
|
28
|
+
|
|
29
|
+
Follow Nitro file-based routing. The filename determines the HTTP method and
|
|
30
|
+
path:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
server/api/my-resource.get.ts → GET /api/my-resource
|
|
34
|
+
server/api/my-resource.post.ts → POST /api/my-resource
|
|
35
|
+
server/api/my-resource/[id].get.ts → GET /api/my-resource/:id
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Route handler pattern:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
export default defineEventHandler(async (event) => {
|
|
42
|
+
const params = getQuery(event); // query string
|
|
43
|
+
const body = await readBody(event); // POST body
|
|
44
|
+
const id = getRouterParam(event, 'id'); // path params
|
|
45
|
+
|
|
46
|
+
// ... implementation ...
|
|
47
|
+
return { result: 'data' };
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Key Differences from Client-Side Code
|
|
52
|
+
|
|
53
|
+
- Server routes run on the server (Node.js), not in the browser
|
|
54
|
+
- They have access to Redis, Neon Postgres, secrets, and server-only APIs
|
|
55
|
+
- They do NOT have access to Vue composables, Vuetify, or any client-side code
|
|
56
|
+
- Use `defineEventHandler`, not Vue component patterns
|
|
57
|
+
|
|
58
|
+
See [architecture.md](architecture.md) in this skill for the full data
|
|
59
|
+
architecture overview, [storage.md](storage.md) for KV/Postgres patterns,
|
|
60
|
+
and [pref.md](pref.md) for client-side KV preferences.
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Error recovery and build failure troubleshooting. Apply when something broke, build failed, npm run build errors, or user wants to restore previous behavior."
|
|
3
|
-
alwaysApply: false
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# Restoring Broken Functionality from Git History
|
|
7
2
|
|
|
8
3
|
**Trigger phrases:** "this used to work", "this broke", "it was working before", "I want it back the way it was", "it looked right before", or similar.
|
|
@@ -12,6 +7,7 @@ alwaysApply: false
|
|
|
12
7
|
## 1. Gather Context
|
|
13
8
|
|
|
14
9
|
Ask the user:
|
|
10
|
+
|
|
15
11
|
- What specifically broke or changed?
|
|
16
12
|
- When do they remember it last working? (A rough timeframe, a branch, a specific action, etc.)
|
|
17
13
|
|
|
@@ -33,7 +29,7 @@ Show the user the combined result (current code + restored pieces) and have them
|
|
|
33
29
|
|
|
34
30
|
## 5. Commit the Restoration
|
|
35
31
|
|
|
36
|
-
Only after the user confirms the restored version is correct, follow the standard git workflow (see
|
|
32
|
+
Only after the user confirms the restored version is correct, follow the standard git workflow (see [git-support.md](git-support.md) in this skill) to commit the changes.
|
|
37
33
|
|
|
38
34
|
# Common Build Errors
|
|
39
35
|
|
|
@@ -49,7 +45,7 @@ import { myHelper } from '~/utils/myHelper';
|
|
|
49
45
|
|
|
50
46
|
### `Type 'X' is not assignable to type 'Y'`
|
|
51
47
|
|
|
52
|
-
Usually an API response shape mismatch. Common case: `getSchema()` nests data under `response.schema` but TypeScript types suggest top-level access. See
|
|
48
|
+
Usually an API response shape mismatch. Common case: `getSchema()` nests data under `response.schema` but TypeScript types suggest top-level access. See [data.md](data.md)'s API Gotchas section in this skill.
|
|
53
49
|
|
|
54
50
|
### `SyntaxError` or blank page with "missing export"
|
|
55
51
|
|