@yottagraph-app/aether-instructions 1.1.42 → 1.1.44

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.
Files changed (38) hide show
  1. package/AGENTS.md +12 -30
  2. package/CLAUDE.md +1 -0
  3. package/README.md +13 -9
  4. package/commands/build_my_app.md +7 -8
  5. package/commands/deploy_agent.md +7 -7
  6. package/commands/update_branding.md +1 -1
  7. package/commands/update_instructions.md +34 -21
  8. package/package.json +4 -5
  9. package/skills/aether/SKILL.md +57 -0
  10. package/{rules/agents-data.mdc → skills/aether/agents-data.md} +3 -6
  11. package/{rules/agents.mdc → skills/aether/agents.md} +42 -28
  12. package/{rules/architecture.mdc → skills/aether/architecture.md} +14 -20
  13. package/{rules/branding.mdc → skills/aether/branding.md} +7 -12
  14. package/{rules/cookbook-data.mdc → skills/aether/cookbook-data.md} +13 -28
  15. package/{rules/cookbook.mdc → skills/aether/cookbook.md} +2 -9
  16. package/{rules/cursor-cloud.mdc → skills/aether/cursor-cloud.md} +1 -6
  17. package/{rules/data.mdc → skills/aether/data.md} +80 -70
  18. package/{rules/deployment.mdc → skills/aether/deployment.md} +1 -6
  19. package/{rules/design.mdc → skills/aether/design.md} +1 -6
  20. package/{rules/env.mdc → skills/aether/env.md} +0 -5
  21. package/{rules/git-support.mdc → skills/aether/git-support.md} +4 -9
  22. package/{rules/instructions_warning.mdc → skills/aether/instructions_warning.md} +12 -13
  23. package/{rules/local-setup.mdc → skills/aether/local-setup.md} +1 -6
  24. package/{rules/mcp-servers.mdc → skills/aether/mcp-servers.md} +3 -7
  25. package/{rules/pref.mdc → skills/aether/pref.md} +9 -14
  26. package/skills/aether/server-data.md +48 -0
  27. package/skills/aether/server.md +60 -0
  28. package/{rules/something-broke.mdc → skills/aether/something-broke.md} +3 -7
  29. package/{rules/server.mdc → skills/aether/storage.md} +78 -108
  30. package/{rules/ui.mdc → skills/aether/ui.md} +2 -6
  31. package/skills/elemental-mcp-patterns/SKILL.md +57 -51
  32. package/variants/mcp-only/commands/build_my_app.md +6 -6
  33. package/variants/mcp-only/{rules/agents-data.mdc → skills/aether/agents-data.md} +0 -6
  34. package/variants/mcp-only/{rules/cookbook-data.mdc → skills/aether/cookbook-data.md} +3 -6
  35. package/variants/mcp-only/{rules/data.mdc → skills/aether/data.md} +1 -6
  36. package/variants/mcp-only/{rules/server-data.mdc → skills/aether/server-data.md} +9 -15
  37. package/rules/server-data.mdc +0 -54
  38. package/rules/storage.mdc +0 -54
@@ -27,7 +27,7 @@ from google.adk.tools.mcp_tool import McpToolset
27
27
  from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams
28
28
  ```
29
29
 
30
- > **Do NOT use `SseConnectionParams`.** `SseConnectionParams` is for
30
+ > **Do NOT use `SseConnectionParams`.** `SseConnectionParams` is for
31
31
  > legacy SSE-based MCP servers. Using it against the Elemental MCP server
32
32
  > will silently fail — the agent starts with zero tools and no error is
33
33
  > raised. The LLM then hallucinates tool calls.
@@ -95,12 +95,14 @@ McpToolset(
95
95
 
96
96
  If `McpToolset` cannot connect, **no error is raised at agent startup**.
97
97
  The agent simply has zero MCP tools. Symptoms:
98
+
98
99
  - The agent never calls any `elemental_*` tools
99
100
  - The LLM fabricates code or data instead of using tools
100
101
  - No connection error in logs
101
102
 
102
103
  **Always validate** the MCP URL and check for tool availability during
103
104
  development. If MCP tools aren't working, verify:
105
+
104
106
  1. The URL is correct (check `broadchurch.yaml` `mcp.elemental` or env var)
105
107
  2. You're using `StreamableHTTPConnectionParams` (not `SseConnectionParams`)
106
108
  3. The MCP server is reachable (try `curl -X POST <url> -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'`)
@@ -120,42 +122,42 @@ adk web
120
122
 
121
123
  ## Tool Quick Reference
122
124
 
123
- | Tool | Use when you need to... |
124
- |---|---|
125
- | `elemental_get_entity` | Resolve an entity by name or ID, fetch its properties (supports `history` for time-series) |
126
- | `elemental_get_related` | Find related entities (requires `related_flavor`); use `direction` and `relationship_types` to filter |
127
- | `elemental_get_events` | Get typed events with categories, dates, participants |
128
- | `elemental_get_citations` | Look up provenance for `ref` hashes returned by other tools |
129
- | `elemental_get_schema` | Discover flavors (entity types), property names, and property types |
130
- | `elemental_get_relationships` | Get relationship types and counts between two entities |
131
- | `elemental_graph_neighborhood` | Get the most influential neighbors of an entity |
132
- | `elemental_graph_sentiment` | Sentiment time series, trend analysis, and statistics from news articles |
133
- | `elemental_introspect` | Discover what data **actually exists**: entity counts, populated properties with fill rates, sample values. Use before building features to verify data availability. |
134
- | `elemental_traverse` | Stateful graph navigation — build a working set of entities across multiple calls (start → expand → filter → inspect) |
135
- | `elemental_health` | Health check — verify MCP server connectivity |
125
+ | Tool | Use when you need to... |
126
+ | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
127
+ | `elemental_get_entity` | Resolve an entity by name or ID, fetch its properties (supports `history` for time-series) |
128
+ | `elemental_get_related` | Find related entities (requires `related_flavor`); use `direction` and `relationship_types` to filter |
129
+ | `elemental_get_events` | Get typed events with categories, dates, participants |
130
+ | `elemental_get_citations` | Look up provenance for `ref` hashes returned by other tools |
131
+ | `elemental_get_schema` | Discover flavors (entity types), property names, and property types |
132
+ | `elemental_get_relationships` | Get relationship types and counts between two entities |
133
+ | `elemental_graph_neighborhood` | Get the most influential neighbors of an entity |
134
+ | `elemental_graph_sentiment` | Sentiment time series, trend analysis, and statistics from news articles |
135
+ | `elemental_introspect` | Discover what data **actually exists**: entity counts, populated properties with fill rates, sample values. Use before building features to verify data availability. |
136
+ | `elemental_traverse` | Stateful graph navigation — build a working set of entities across multiple calls (start → expand → filter → inspect) |
137
+ | `elemental_health` | Health check — verify MCP server connectivity |
136
138
 
137
139
  ### MCP Prompts
138
140
 
139
141
  The server also exposes built-in prompts that provide pre-composed
140
142
  workflows:
141
143
 
142
- | Prompt | Purpose |
143
- |---|---|
144
- | `company-deep-dive` | Comprehensive company research workflow |
145
- | `blast-radius` | Analyze impact/connections radiating from an entity |
146
- | `event-monitor` | Track events and developments for entities |
144
+ | Prompt | Purpose |
145
+ | ------------------- | --------------------------------------------------- |
146
+ | `company-deep-dive` | Comprehensive company research workflow |
147
+ | `blast-radius` | Analyze impact/connections radiating from an entity |
148
+ | `event-monitor` | Track events and developments for entities |
147
149
 
148
150
  ### MCP Resources
149
151
 
150
152
  Documentation resources are available directly from the server:
151
153
 
152
- | Resource | Description |
153
- |---|---|
154
- | `elemental_data_model` | Entity types, properties, and relationships |
155
- | `elemental_guide` | Usage guide for the MCP tools |
156
- | `elemental_schema` | Live schema data |
157
- | `elemental_workflows` | Common workflow patterns and examples |
158
- | `elemental_mcp_server_info` | Server capabilities and configuration |
154
+ | Resource | Description |
155
+ | --------------------------- | ------------------------------------------- |
156
+ | `elemental_data_model` | Entity types, properties, and relationships |
157
+ | `elemental_guide` | Usage guide for the MCP tools |
158
+ | `elemental_schema` | Live schema data |
159
+ | `elemental_workflows` | Common workflow patterns and examples |
160
+ | `elemental_mcp_server_info` | Server capabilities and configuration |
159
161
 
160
162
  These prompts and resources can be useful shortcuts — check if your MCP
161
163
  client supports them before building equivalent logic from scratch.
@@ -204,6 +206,7 @@ result = await mcp_call("elemental_get_entity", {
204
206
  ```
205
207
 
206
208
  Key points:
209
+
207
210
  - `entity` is `null` if resolution failed
208
211
  - Each property value is `{"value": ..., "ref"?: "...", "attributes"?: {...}}`
209
212
  - **`value` can be a NEID** for reference-typed properties — see
@@ -247,6 +250,7 @@ result = await mcp_call("elemental_get_related", {
247
250
  ```
248
251
 
249
252
  Key points:
253
+
250
254
  - `related_flavor` is **required** — you must specify what type of entity
251
255
  to look for
252
256
  - `resolved` is the center entity (can be `null` if resolution failed)
@@ -298,6 +302,7 @@ result = await mcp_call("elemental_get_events", {
298
302
  ```
299
303
 
300
304
  Key points:
305
+
301
306
  - Events have typed fields: `category`, `date`, `description`, `likelihood`
302
307
  - Use `categories` to filter — do NOT try to find events by scanning
303
308
  property names for keywords
@@ -383,13 +388,13 @@ def _format_large_number(n: float) -> str:
383
388
 
384
389
  ### Property type values you'll encounter
385
390
 
386
- | Schema type | Value is | How to display |
387
- |---|---|---|
388
- | `string` | Plain text | Display directly |
389
- | `integer`, `float` | Number | Format with units (check `unit` in schema) |
390
- | `nindex` | Entity NEID | **Must resolve** via `elemental_get_entity` |
391
- | `boolean` | `true`/`false` | Display as Yes/No |
392
- | `datetime` | ISO 8601 string | Format as human-readable date |
391
+ | Schema type | Value is | How to display |
392
+ | ------------------ | --------------- | ------------------------------------------- |
393
+ | `string` | Plain text | Display directly |
394
+ | `integer`, `float` | Number | Format with units (check `unit` in schema) |
395
+ | `nindex` | Entity NEID | **Must resolve** via `elemental_get_entity` |
396
+ | `boolean` | `true`/`false` | Display as Yes/No |
397
+ | `datetime` | ISO 8601 string | Format as human-readable date |
393
398
 
394
399
  ---
395
400
 
@@ -538,14 +543,14 @@ tables below are a starting-point cheat sheet — not exhaustive.
538
543
 
539
544
  ### Properties by flavor
540
545
 
541
- | Flavor | Common properties |
542
- |---|---|
543
- | `organization` | `country` (nindex), `ticker_symbol`, `total_revenue`, `net_income`, `total_assets`, `industry` (nindex), `lei`, `company_cik`, `ein`, `website` |
544
- | `person` | `nationality` (nindex), `title`, `birth_date`, `gender` |
545
- | `government_body` | `country` (nindex), `jurisdiction` |
546
- | `article` | `headline`, `published_date`, `source`, `url` |
547
- | `event` | `category`, `date`, `description`, `likelihood` |
548
- | `financial_instrument` | `ticker_symbol`, `exchange`, `currency` |
546
+ | Flavor | Common properties |
547
+ | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
548
+ | `organization` | `country` (nindex), `ticker_symbol`, `total_revenue`, `net_income`, `total_assets`, `industry` (nindex), `lei`, `company_cik`, `ein`, `website` |
549
+ | `person` | `nationality` (nindex), `title`, `birth_date`, `gender` |
550
+ | `government_body` | `country` (nindex), `jurisdiction` |
551
+ | `article` | `headline`, `published_date`, `source`, `url` |
552
+ | `event` | `category`, `date`, `description`, `likelihood` |
553
+ | `financial_instrument` | `ticker_symbol`, `exchange`, `currency` |
549
554
 
550
555
  Properties marked `(nindex)` are entity references — their raw value is
551
556
  a NEID that must be resolved to a display name. See "The Property Type
@@ -556,16 +561,16 @@ Problem" above.
556
561
  The `direction` parameter on `elemental_get_related` controls traversal.
557
562
  Getting it wrong returns zero results with no error.
558
563
 
559
- | Relationship | Meaning | Direction from center |
560
- |---|---|---|
561
- | `board_member_of` | Person sits on org's board | `"incoming"` when center is org |
562
- | `is_officer` | Person is an officer of org | `"incoming"` when center is org |
563
- | `subsidiary_of` | Org is a subsidiary of parent | `"outgoing"` from subsidiary |
564
- | `owns` | Entity owns another entity | `"outgoing"` from owner |
565
- | `appears_in` | Entity mentioned in article | `"both"` is usually safest |
566
- | `participant` | Entity participates in event | `"both"` is usually safest |
567
- | `filed` | Org filed a document | `"outgoing"` from org |
568
- | `works_at` | Person works at org | `"outgoing"` from person |
564
+ | Relationship | Meaning | Direction from center |
565
+ | ----------------- | ----------------------------- | ------------------------------- |
566
+ | `board_member_of` | Person sits on org's board | `"incoming"` when center is org |
567
+ | `is_officer` | Person is an officer of org | `"incoming"` when center is org |
568
+ | `subsidiary_of` | Org is a subsidiary of parent | `"outgoing"` from subsidiary |
569
+ | `owns` | Entity owns another entity | `"outgoing"` from owner |
570
+ | `appears_in` | Entity mentioned in article | `"both"` is usually safest |
571
+ | `participant` | Entity participates in event | `"both"` is usually safest |
572
+ | `filed` | Org filed a document | `"outgoing"` from org |
573
+ | `works_at` | Person works at org | `"outgoing"` from person |
569
574
 
570
575
  > **Tip:** If you're unsure about direction, use `"both"` (the default)
571
576
  > first and check the results. Then narrow to `"incoming"` or
@@ -627,6 +632,7 @@ Revenue was $52.9B [ref_a3f2b1c8]
627
632
  ```
628
633
 
629
634
  The chat UI translates these into numbered source citations. Rules:
635
+
630
636
  - Only include refs that are present in the actual response data
631
637
  - Copy the ref string exactly — never construct or modify refs
632
638
  - Omit the bracket when no ref is present for a value
@@ -11,7 +11,7 @@ Read the project brief and build the described application.
11
11
  - **Channeling** — Agents invoke MCP tools and return **natural-language** answers to the user (often chat-first; minimal or no Postgres for graph data).
12
12
  - **Mixes** — e.g. a sync job for dashboards plus a research agent plus a channel agent for Q&A.
13
13
 
14
- Read the **`data`** and **`agents-data`** cursor rules for detail.
14
+ Read [`data.md`](../skills/aether/data.md) and [`agents-data.md`](../skills/aether/agents-data.md) in the `aether` skill for detail.
15
15
 
16
16
  **This is meant to be the first thing a user runs after opening their project in Cursor.**
17
17
 
@@ -78,7 +78,7 @@ If the file exists but tools aren't active yet:
78
78
  >
79
79
  > Open **Cursor Settings** (Cmd+Shift+J on macOS) → **Tools & MCP** and enable the `lovelace-*` servers listed there. They should show green toggles when active. Let me know when they're enabled (or if you'd like to skip this).
80
80
 
81
- Wait for confirmation before proceeding if you asked the user to enable servers. If the user skips or the panel isn't available (e.g. Cursor Cloud), proceed — you can still follow the **`data`** rule and ship agents that use MCP at **runtime**.
81
+ Wait for confirmation before proceeding if you asked the user to enable servers. If the user skips or the panel isn't available (e.g. Cursor Cloud), proceed — you can still follow [`data.md`](../skills/aether/data.md) in the `aether` skill and ship agents that use MCP at **runtime**.
82
82
 
83
83
  The **Nuxt** layer may combine **your APIs** (often Postgres-backed), **chat** via **`useAgentChat`**, or both — not Elemental REST from Vue for graph data.
84
84
 
@@ -92,9 +92,9 @@ Read:
92
92
 
93
93
  1. `DESIGN.md` — which of the valid shapes above (or a mix) the brief implies
94
94
  2. `broadchurch.yaml`
95
- 3. **`data` rule** — MCP-only patterns
96
- 4. **`agents-data` rule** — wiring MCP into ADK
97
- 5. **`cookbook-data` / `server` / `server-data`** — when the UI lists data from **your** APIs / Postgres
95
+ 3. [`data.md`](../skills/aether/data.md) in the `aether` skill — MCP-only patterns
96
+ 4. [`agents-data.md`](../skills/aether/agents-data.md) in the `aether` skill — wiring MCP into ADK
97
+ 5. [`cookbook-data.md`](../skills/aether/cookbook-data.md), [`server.md`](../skills/aether/server.md) and [`storage.md`](../skills/aether/storage.md), [`server-data.md`](../skills/aether/server-data.md) in the `aether` skill — when the UI lists data from **your** APIs (server routes) or **Postgres** (storage)
98
98
 
99
99
  ---
100
100
 
@@ -119,7 +119,7 @@ research assistant, investigation tool, etc.), build the agent FIRST:
119
119
 
120
120
  1. **`agents/`** — Build the full agent with all tools specified in DESIGN.md.
121
121
  Each named tool must be a Python function — do NOT just pass `McpToolset`
122
- through and write a system prompt. Read the `agents` rule ("McpToolset
122
+ through and write a system prompt. Read [`agents.md`](../skills/aether/agents.md) in the `aether` skill ("McpToolset
123
123
  passthrough" section) for why this matters.
124
124
  2. Test the agent locally with `adk web` before building the UI.
125
125
  3. Then build the UI around the working agent.
@@ -1,9 +1,3 @@
1
- ---
2
- description: "MCP-only: ADK agents use Elemental MCP for sync, research, investigation, aggregation, or channeling — Postgres optional."
3
- alwaysApply: false
4
- globs: agents/**
5
- ---
6
-
7
1
  # Agents: Elemental MCP (MCP-only)
8
2
 
9
3
  In **mcp-only** mode, **Elemental MCP** is the primary way agents reach the knowledge graph. **`broadchurch_auth` HTTP** to the Query Server is the **api-mcp** path; here you wire **MCP tools** into ADK via **McpToolset** + **`StreamableHTTPConnectionParams`** to your Elemental MCP URL — from env or `broadchurch.yaml` / gateway.
@@ -1,8 +1,3 @@
1
- ---
2
- description: "MCP-only: UI recipes when data is exposed via your own APIs (often Postgres). Chat-first or agent-only flows may skip these."
3
- alwaysApply: false
4
- ---
5
-
6
1
  # Data-fetching cookbook (MCP-only)
7
2
 
8
3
  **Valid UIs for mcp-only include:** (a) **Chat-first** surfaces where the user talks to an agent that calls MCP tools (`useAgentChat`, agent design — see `data` / `agents-data`). (b) **List/detail/table** screens fed by **your** Nitro APIs, often backed by **Postgres** rows populated by agents that used Elemental MCP upstream.
@@ -19,7 +14,9 @@ Assume composables like `useEntities()` wrap `$fetch('/api/entities')` — adjus
19
14
  <template>
20
15
  <div class="d-flex flex-column fill-height pa-4">
21
16
  <h1 class="text-h5 mb-4">Entities</h1>
22
- <v-alert v-if="error" type="error" variant="tonal" class="mb-4" closable>{{ error }}</v-alert>
17
+ <v-alert v-if="error" type="error" variant="tonal" class="mb-4" closable>{{
18
+ error
19
+ }}</v-alert>
23
20
  <v-data-table :items="items" :loading="loading" />
24
21
  </div>
25
22
  </template>
@@ -1,8 +1,3 @@
1
- ---
2
- description: "MCP-only data mode: no Elemental REST client in Nuxt; Elemental MCP is used by ADK agents — sync, research, or direct channeling."
3
- alwaysApply: false
4
- ---
5
-
6
1
  # Data — MCP-only (agents + Elemental MCP, not the Elemental API in the SPA)
7
2
 
8
3
  In **mcp-only** mode the Nuxt app does **not** call the Elemental API via `useElementalClient()` or gateway REST from the browser. **Elemental MCP** is how custom **ADK agents** reach the Lovelace knowledge graph.
@@ -28,7 +23,7 @@ In **mcp-only** mode the Nuxt app does **not** call the Elemental API via `useEl
28
23
  If you **do** use the sync pattern, treat Neon as the **read model** for those pages:
29
24
 
30
25
  1. **`DATABASE_URL`** — Use `getDb()` from `~/server/utils/neon.ts` in server routes.
31
- 2. **Schema** — `CREATE TABLE IF NOT EXISTS` / `ensureTables()` (see `server` rule). Align columns with the **data-model** skill where relevant.
26
+ 2. **Schema** — `CREATE TABLE IF NOT EXISTS` / `ensureTables()` (see [storage.md](storage.md) in this skill). Align columns with the **data-model** skill where relevant.
32
27
  3. **Composables** — Thin wrappers around `$fetch('/api/...')` to your own APIs (`useEntities`, etc.).
33
28
  4. **Agents that populate tables** — Call Elemental MCP tools, map to rows, **UPSERT** idempotently. Document env (`DATABASE_URL`, MCP URL) in `DESIGN.md` or the agent README.
34
29
 
@@ -1,14 +1,8 @@
1
- ---
2
- description: "MCP-only: Nitro routes that read your own store (often Neon). Not used for proxying Elemental REST to the SPA."
3
- alwaysApply: false
4
- globs: server/**
5
- ---
6
-
7
1
  # Server routes: your data store (MCP-only)
8
2
 
9
3
  In mcp-only mode, **server routes do not** implement the **Elemental REST** proxy pattern from api-mcp for the **primary** SPA data path. Graph access lives in **agents + Elemental MCP**.
10
4
 
11
- This rule applies when your architecture includes a **local read model** (usually **Neon Postgres**) filled by agents that called MCP upstream. **Chat-only** or **agent-only** products may have **few or no** such routes — that is fine.
5
+ This guidance applies when your architecture includes a **local read model** (usually **Neon Postgres**) filled by agents that called MCP upstream. **Chat-only** or **agent-only** products may have **few or no** such routes — that is fine.
12
6
 
13
7
  ## Pattern (Postgres-backed lists / detail)
14
8
 
@@ -16,24 +10,24 @@ This rule applies when your architecture includes a **local read model** (usuall
16
10
  import { getDb } from '~/server/utils/neon';
17
11
 
18
12
  export default defineEventHandler(async (event) => {
19
- const sql = getDb();
20
- if (!sql) throw createError({ statusCode: 503, statusMessage: 'Database not configured' });
13
+ const sql = getDb();
14
+ if (!sql) throw createError({ statusCode: 503, statusMessage: 'Database not configured' });
21
15
 
22
- const q = getQuery(event).q as string | undefined;
23
- if (q) {
24
- return await sql`
16
+ const q = getQuery(event).q as string | undefined;
17
+ if (q) {
18
+ return await sql`
25
19
  SELECT neid, name, updated_at FROM entities
26
20
  WHERE name ILIKE ${'%' + q + '%'}
27
21
  ORDER BY updated_at DESC LIMIT 50
28
22
  `;
29
- }
30
- return await sql`SELECT neid, name, updated_at FROM entities ORDER BY updated_at DESC LIMIT 50`;
23
+ }
24
+ return await sql`SELECT neid, name, updated_at FROM entities ORDER BY updated_at DESC LIMIT 50`;
31
25
  });
32
26
  ```
33
27
 
34
28
  ## `ensureTables`
35
29
 
36
- Call a shared `ensureTables()` before reads if tables might be missing on cold start (see `server` rule).
30
+ Call a shared `ensureTables()` before reads if tables might be missing on cold start (see [storage.md](storage.md) in this skill).
37
31
 
38
32
  ## Secrets
39
33
 
@@ -1,54 +0,0 @@
1
- ---
2
- description: "Calling the Elemental API from Nitro server routes via the Portal Gateway. Read when proxying Query Server calls server-side."
3
- alwaysApply: false
4
- globs: server/**
5
- ---
6
-
7
- # Server routes: Elemental API (Query Server)
8
-
9
- Server routes can call the Elemental API through the Portal Gateway proxy,
10
- just like client-side code does. The gateway URL, tenant org ID, and API key
11
- are available via `useRuntimeConfig()`.
12
-
13
- **NEVER use `readFileSync('broadchurch.yaml')` in server routes.** The YAML
14
- file is read at build time by `nuxt.config.ts` and its values flow into
15
- `runtimeConfig`. Nitro serverless functions (Vercel) don't bundle arbitrary
16
- project files — `readFileSync` will crash with ENOENT in production even
17
- though it works locally.
18
-
19
- ```typescript
20
- export default defineEventHandler(async (event) => {
21
- const { public: config } = useRuntimeConfig();
22
-
23
- const gatewayUrl = config.gatewayUrl; // Portal Gateway base URL
24
- const orgId = config.tenantOrgId; // Tenant org ID (path segment)
25
- const apiKey = config.qsApiKey; // API key for X-Api-Key header
26
-
27
- if (!gatewayUrl || !orgId) {
28
- throw createError({ statusCode: 503, statusMessage: 'Gateway not configured' });
29
- }
30
-
31
- const res = await $fetch(`${gatewayUrl}/api/qs/${orgId}/entities/search`, {
32
- method: 'POST',
33
- headers: {
34
- 'Content-Type': 'application/json',
35
- ...(apiKey && { 'X-Api-Key': apiKey }),
36
- },
37
- body: { queries: [{ queryId: 1, query: 'Microsoft' }], maxResults: 5 },
38
- });
39
-
40
- return res;
41
- });
42
- ```
43
-
44
- Available runtime config keys (all under `runtimeConfig.public`):
45
-
46
- | Key | Source | Purpose |
47
- |---|---|---|
48
- | `gatewayUrl` | `broadchurch.yaml` → `gateway.url` | Portal Gateway base URL |
49
- | `tenantOrgId` | `broadchurch.yaml` → `tenant.org_id` | Tenant ID for API path |
50
- | `qsApiKey` | `broadchurch.yaml` → `gateway.qs_api_key` | API key sent as `X-Api-Key` |
51
- | `queryServerAddress` | `broadchurch.yaml` → `query_server.url` | Direct QS URL (prefer gateway) |
52
-
53
- Build the request URL as `{gatewayUrl}/api/qs/{tenantOrgId}/{endpoint}`.
54
- See the `data` rule for endpoint reference and response shapes.
package/rules/storage.mdc DELETED
@@ -1,54 +0,0 @@
1
- ---
2
- description: Storage backends available to the app (KV/Upstash Redis always on; Neon Postgres if provisioned). Apply when choosing persistence, deciding if Postgres is available, or wiring up getRedis()/getDb().
3
- alwaysApply: false
4
- ---
5
-
6
- Two storage services are available. Check `.env` to see which are connected:
7
-
8
- | Store | How to check | Env var | Utility file | Always available? |
9
- | ---------------------- | --------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------- | ----------------------------------- |
10
- | **KV** (Upstash Redis) | `KV_REST_API_URL` in `.env` | `KV_REST_API_URL`, `KV_REST_API_TOKEN` | `server/utils/redis.ts` (pre-scaffolded) | Yes |
11
- | **Neon Postgres** | `curl <gateway.url>/api/tenants/<tenant.org_id>` → `vercel.postgres_store_id` set | `DATABASE_URL`, `DATABASE_URL_UNPOOLED` | `server/utils/neon.ts` (create it if missing — see below) | Only if enabled at project creation |
12
-
13
- ### Quick start
14
-
15
- **KV** is ready to use out of the box. Use `getRedis()` from
16
- `server/utils/redis.ts` in server routes, or `usePrefsStore()` on the client
17
- (see `pref` rule for the `Pref<T>` pattern).
18
-
19
- **Neon Postgres** — provisioning is determined by the portal, not by `.env`.
20
- To check whether Neon is enabled for this tenant, pull `gateway.url` and
21
- `tenant.org_id` out of `broadchurch.yaml` and query the portal:
22
-
23
- ```bash
24
- curl <gateway.url>/api/tenants/<tenant.org_id>
25
- ```
26
-
27
- If the response has `vercel.postgres_store_id` set, Neon is provisioned. The
28
- `DATABASE_URL` is also present under `agent_secrets` in that response, but
29
- you usually do not need to read it directly.
30
-
31
- `DATABASE_URL` and `DATABASE_URL_UNPOOLED` are intentionally left commented
32
- out in local `.env` — Neon does not work locally. On deploy, Vercel
33
- auto-injects `DATABASE_URL` at runtime, so pushed builds connect
34
- transparently.
35
-
36
- For the `getDb()` lazy-init pattern, `@neondatabase/serverless` install, and
37
- full code examples (creating tables, handling missing tables in GET routes,
38
- etc.), see the `server` rule.
39
-
40
- ### Where credentials come from
41
-
42
- **Deployed builds** (push to `main` → Vercel): storage env vars are
43
- auto-injected and decrypted at runtime. Storage works with zero
44
- configuration. **This is the primary development path** — push your code
45
- and test on the deployed preview/production URL.
46
-
47
- **Local dev / Cursor Cloud:** storage credentials are not yet available for
48
- local use. `getRedis()` and `getDb()` will return `null`, and the app should
49
- handle this gracefully (show a "not configured" state, use defaults, etc.).
50
- KV preferences fall back to their default values. Postgres features should
51
- check `getDb()` and show appropriate UI when it returns `null`.
52
-
53
- This is a known platform limitation — the Broadchurch team is working on
54
- making storage credentials available for local development.