@genlobe/mcp-server 3.1.3 → 3.3.0
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/README.md +0 -29
- package/dist/index.js +223 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -159,35 +159,6 @@ To point your IDE at a local build instead of npm:
|
|
|
159
159
|
|
|
160
160
|
---
|
|
161
161
|
|
|
162
|
-
## Releasing a new version
|
|
163
|
-
|
|
164
|
-
The MCP version follows semver and is bumped independently of the backend API:
|
|
165
|
-
|
|
166
|
-
- **patch** — bug fix in a tool's behavior, doc correction
|
|
167
|
-
- **minor** — new tool, new optional field, new env var with a default
|
|
168
|
-
- **major** — tool removed/renamed, env var removed, breaking change in returned shape
|
|
169
|
-
|
|
170
|
-
Publish flow (works around the subtree quirk where `npm version` doesn't always commit cleanly):
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
cd mcp-server
|
|
174
|
-
# 1. Move CHANGELOG [Unreleased] section under the new version header.
|
|
175
|
-
# 2. Bump versions without letting npm touch git:
|
|
176
|
-
npm version <patch|minor|major> --no-git-tag-version
|
|
177
|
-
# 3. One commit + one tag, both intentional:
|
|
178
|
-
git add package.json package-lock.json CHANGELOG.md
|
|
179
|
-
git commit -m "chore(mcp): release vX.Y.Z"
|
|
180
|
-
git tag -a vX.Y.Z -m "@genlobe/mcp-server vX.Y.Z"
|
|
181
|
-
# 4. Publish (prepublishOnly runs `tsc` automatically):
|
|
182
|
-
npm publish --access public
|
|
183
|
-
# 5. Push commit + tag:
|
|
184
|
-
git push origin main && git push origin vX.Y.Z
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
The CI workflow `.github/workflows/publish-mcp.yml` automates steps 4–5 when you push a `mcp-v*` tag (see [Automated publishing](#automated-publishing) below).
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
162
|
## License
|
|
192
163
|
|
|
193
164
|
MIT — see [package.json](./package.json).
|
package/dist/index.js
CHANGED
|
@@ -64,8 +64,13 @@ There are TWO types of authentication contexts:
|
|
|
64
64
|
- These endpoints ARE for building end-user frontends
|
|
65
65
|
|
|
66
66
|
## API Key Types
|
|
67
|
-
|
|
68
|
-
- \`
|
|
67
|
+
|
|
68
|
+
- \`pk_live_*\`: Public key — safe to expose in frontend code.
|
|
69
|
+
- **By itself** (no JWT) the key only unlocks \`/v1/auth/*\` (register, login, refresh, logout, me, forgot-password, reset-password). These are the seven scopes auto-assigned to public keys.
|
|
70
|
+
- **Combined with an end-user JWT** returned by \`/v1/auth/login\`, the same public key unlocks the full end-user API: \`/v1/user/agents/*\`, \`/v1/entity/records/*\`, \`/v1/plans\`, \`/v1/billing/*\`, \`/v1/users/me\`, etc. Once the JWT is in play, the JWT carries end-user identity and the public key just keeps verifying the tenant context.
|
|
71
|
+
- Restricted by an Origin allowlist (configured per key) and per-IP rate limits.
|
|
72
|
+
|
|
73
|
+
- \`sk_live_*\`: Secret key — backend-only, never expose. Required for server-to-server work that no end-user JWT can cover: defining entity schemas (\`/v1/entity/schemas/*\`), bulk record operations (\`/v1/entity/records/bulk\`, \`/v1/entity/records/search\`), webhooks, batch jobs, and other admin tooling on the tenant's own server.
|
|
69
74
|
|
|
70
75
|
## API Key Organization Scope (B2B2C)
|
|
71
76
|
|
|
@@ -102,6 +107,38 @@ If you're building a frontend application for end-users, you should:
|
|
|
102
107
|
2. Use the public API key (pk_live_*) for client-side requests
|
|
103
108
|
3. Include X-API-Key header in ALL requests
|
|
104
109
|
4. After login, also include Authorization: Bearer <jwt> for authenticated requests
|
|
110
|
+
|
|
111
|
+
## End-User App Flow (the canonical two-phase pattern)
|
|
112
|
+
|
|
113
|
+
A common confusion: the public key looks restricted to \`/v1/auth/*\`, so developers assume they need to ship the secret key in their frontend to call agents or records. They don't. The pattern is two phases:
|
|
114
|
+
|
|
115
|
+
\`\`\`
|
|
116
|
+
[Tenant's app frontend in the user's browser]
|
|
117
|
+
│
|
|
118
|
+
│ Phase 1 — auth gateway (public key alone is enough)
|
|
119
|
+
│ X-API-Key: pk_live_xxx
|
|
120
|
+
▼
|
|
121
|
+
POST /v1/auth/register ─┐
|
|
122
|
+
POST /v1/auth/login ├─ returns { access_token (JWT), refresh_token, user }
|
|
123
|
+
POST /v1/auth/refresh ─┘
|
|
124
|
+
│
|
|
125
|
+
│ Phase 2 — end-user features (public key + JWT)
|
|
126
|
+
│ X-API-Key: pk_live_xxx
|
|
127
|
+
│ Authorization: Bearer <end-user JWT>
|
|
128
|
+
▼
|
|
129
|
+
GET /v1/user/agents/available
|
|
130
|
+
POST /v1/user/agents/{id}/chat
|
|
131
|
+
GET/POST/PUT/DELETE /v1/entity/records/... ← custom-entity records as your app's DB
|
|
132
|
+
GET /v1/plans
|
|
133
|
+
POST /v1/billing/checkout
|
|
134
|
+
GET /v1/users/me
|
|
135
|
+
\`\`\`
|
|
136
|
+
|
|
137
|
+
The \`sk_live_*\` key never reaches the browser. It lives only on the tenant's own server, used for:
|
|
138
|
+
- defining entity schemas (\`/v1/entity/schemas/*\`),
|
|
139
|
+
- bulk record operations (\`/v1/entity/records/bulk\`, \`/search\`),
|
|
140
|
+
- receiving webhooks from the platform,
|
|
141
|
+
- cron jobs and admin tooling.
|
|
105
142
|
`,
|
|
106
143
|
base_url: API_URL,
|
|
107
144
|
documentation_urls: {
|
|
@@ -787,7 +824,7 @@ const END_USER_ENDPOINTS = {
|
|
|
787
824
|
]
|
|
788
825
|
},
|
|
789
826
|
billing: {
|
|
790
|
-
description: "Billing operations -
|
|
827
|
+
description: "Billing operations. Two flavors:\n\n- **Tier 1 (legacy / platform-level)**: endpoints under /v1/billing/* use the platform's Stripe and a SECRET key (sk_live_*) only. They were originally for Genlobe → Tenant billing and a since-superseded Tier 2 prototype; keep using them for tenant-internal flows.\n\n- **Tier 3 (Organization → end-user, Epic #209)**: each Organization runs its OWN Stripe (BYO key). End-users subscribe via /v1/organizations/{org_id}/billing/* (api key + JWT + active OrganizationMember). The Org's plan catalog is read via the public /v1/organizations/{org_id}/plans (api key only — no JWT). See the master spec at specs/features/billing/three-tier-billing.spec.md.",
|
|
791
828
|
endpoints: [
|
|
792
829
|
{
|
|
793
830
|
method: "GET",
|
|
@@ -880,6 +917,91 @@ const END_USER_ENDPOINTS = {
|
|
|
880
917
|
total_invoices: "number"
|
|
881
918
|
},
|
|
882
919
|
note: "Returns 404 if organization not found or doesn't belong to tenant."
|
|
920
|
+
},
|
|
921
|
+
// ---------------------------------------------------------------
|
|
922
|
+
// Tier 3 endpoints (Epic #209). Each Organization runs its OWN
|
|
923
|
+
// Stripe (BYO key). These run against the org's Stripe, not the
|
|
924
|
+
// platform's, and require an active OrganizationMember of the path's
|
|
925
|
+
// org. Plans are sold by the Organization to its end-users.
|
|
926
|
+
// ---------------------------------------------------------------
|
|
927
|
+
{
|
|
928
|
+
method: "GET",
|
|
929
|
+
path: "/v1/organizations/{organization_id}/plans",
|
|
930
|
+
summary: "List the Organization's active plans (Tier 3) — public to anonymous visitors of the org's app",
|
|
931
|
+
auth: { api_key: "pk_live_* or sk_live_*", jwt: false },
|
|
932
|
+
response: {
|
|
933
|
+
items: "array of OrganizationPlan: id (uuid), organization_id (uuid), name (string), slug (string), description (string | null), price_amount (decimal), currency (ISO-4217), billing_interval ('month' | 'year'), stripe_product_id (string | null), stripe_price_id (string | null), stripe_price_id_yearly (string | null), features (array of strings), limits (object<string, int | null>), is_active (boolean), created_at (ISO datetime), updated_at (ISO datetime)",
|
|
934
|
+
total: "number"
|
|
935
|
+
},
|
|
936
|
+
note: "Tenant-scoped: returns 404 if the Organization doesn't belong to the API key's tenant (no existence leak across tenants). Only plans with is_active=true and stripe_price_id != null are returned — use one of those plan ids when calling /billing/checkout-session.",
|
|
937
|
+
example_request: `GET /v1/organizations/660e8400-e29b-41d4-a716-446655440001/plans`
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
method: "POST",
|
|
941
|
+
path: "/v1/organizations/{organization_id}/billing/checkout-session",
|
|
942
|
+
summary: "Start a Stripe Checkout subscription flow for the calling end-user against an Org plan (Tier 3)",
|
|
943
|
+
auth: { api_key: "pk_live_* or sk_live_*", jwt: true },
|
|
944
|
+
request_body: {
|
|
945
|
+
plan_id: "uuid (required) — OrganizationPlan id (NOT a Stripe price id). Get it from /v1/organizations/{organization_id}/plans.",
|
|
946
|
+
success_url: "string url (required) — Stripe redirects here on success.",
|
|
947
|
+
cancel_url: "string url (required) — Stripe redirects here if the user cancels."
|
|
948
|
+
},
|
|
949
|
+
response: {
|
|
950
|
+
checkout_url: "string — Stripe-hosted Checkout URL (redirect the user here).",
|
|
951
|
+
session_id: "string — Stripe Checkout session id (cs_*)."
|
|
952
|
+
},
|
|
953
|
+
errors: {
|
|
954
|
+
"403": "Caller is not an active OrganizationMember of the path's org.",
|
|
955
|
+
"404": "Plan not found / belongs to a different org / inactive (collapsed to the same 404 to avoid cross-org existence leaks).",
|
|
956
|
+
"409": "Plan has no stripe_price_id yet OR the Organization has no Stripe configured (the Org Owner must finish setup first)."
|
|
957
|
+
},
|
|
958
|
+
note: "The caller becomes the end-user customer in the Organization's Stripe account, not in the platform's. The actual Subscription row is created by the webhook handler after Stripe fires checkout.session.completed.",
|
|
959
|
+
example_request: `{
|
|
960
|
+
"plan_id": "770e8400-e29b-41d4-a716-446655440002",
|
|
961
|
+
"success_url": "https://claude.example.com/billing/success?cs={CHECKOUT_SESSION_ID}",
|
|
962
|
+
"cancel_url": "https://claude.example.com/pricing"
|
|
963
|
+
}`
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
method: "POST",
|
|
967
|
+
path: "/v1/organizations/{organization_id}/billing/customer-portal-session",
|
|
968
|
+
summary: "Open a Stripe Customer Portal session for the calling end-user (Tier 3, manage / cancel)",
|
|
969
|
+
auth: { api_key: "pk_live_* or sk_live_*", jwt: true },
|
|
970
|
+
request_body: {
|
|
971
|
+
return_url: "string url (required) — Stripe redirects here when the user closes the Portal."
|
|
972
|
+
},
|
|
973
|
+
response: {
|
|
974
|
+
portal_url: "string — Stripe-hosted Customer Portal URL."
|
|
975
|
+
},
|
|
976
|
+
errors: {
|
|
977
|
+
"403": "Caller is not an active OrganizationMember of the path's org.",
|
|
978
|
+
"409": "Caller has no Subscription / no Stripe customer for this Organization yet, OR the Organization has no Stripe configured."
|
|
979
|
+
},
|
|
980
|
+
note: "Portal allowed actions (cancel / update payment method / etc.) are configured by the Organization Owner in their own Stripe Dashboard."
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
method: "GET",
|
|
984
|
+
path: "/v1/organizations/{organization_id}/billing/subscription",
|
|
985
|
+
summary: "Read the calling end-user's current subscription in this Organization (Tier 3)",
|
|
986
|
+
auth: { api_key: "pk_live_* or sk_live_*", jwt: true },
|
|
987
|
+
response: {
|
|
988
|
+
id: "uuid",
|
|
989
|
+
organization_id: "uuid",
|
|
990
|
+
user_id: "uuid",
|
|
991
|
+
plan_id: "uuid | null",
|
|
992
|
+
status: "string | null — Stripe Subscription.status (active, past_due, canceled, …)",
|
|
993
|
+
billing_cycle: "string — 'monthly' or 'yearly'",
|
|
994
|
+
current_period_start: "ISO datetime | null",
|
|
995
|
+
current_period_end: "ISO datetime | null",
|
|
996
|
+
cancel_at_period_end: "boolean",
|
|
997
|
+
canceled_at: "ISO datetime | null",
|
|
998
|
+
trial_start: "ISO datetime | null",
|
|
999
|
+
trial_end: "ISO datetime | null"
|
|
1000
|
+
},
|
|
1001
|
+
errors: {
|
|
1002
|
+
"403": "Caller is not an active OrganizationMember of the path's org.",
|
|
1003
|
+
"404": "Caller has no Subscription for this Organization yet."
|
|
1004
|
+
}
|
|
883
1005
|
}
|
|
884
1006
|
]
|
|
885
1007
|
},
|
|
@@ -1026,6 +1148,29 @@ const END_USER_ENDPOINTS = {
|
|
|
1026
1148
|
error: "string | null"
|
|
1027
1149
|
},
|
|
1028
1150
|
note: "Returns 402 Payment Required if usage limits exceeded."
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
method: "GET",
|
|
1154
|
+
path: "/v1/organizations/{organization_id}/usage",
|
|
1155
|
+
summary: "Get aggregate usage for one Organization in the queried period",
|
|
1156
|
+
auth: { api_key: "pk_live_* or sk_live_*", jwt: true },
|
|
1157
|
+
query_params: {
|
|
1158
|
+
period_start: "ISO datetime (optional) - Inclusive start of the aggregation window (UTC). Defaults to the current calendar month.",
|
|
1159
|
+
period_end: "ISO datetime (optional) - Exclusive end of the aggregation window (UTC). Must be supplied together with period_start.",
|
|
1160
|
+
},
|
|
1161
|
+
response: {
|
|
1162
|
+
organization_id: "uuid",
|
|
1163
|
+
organization_name: "string | null - Human-readable name from organizations row, null if the org row was deleted",
|
|
1164
|
+
organization_slug: "string | null",
|
|
1165
|
+
period_start: "ISO datetime - resolved window start",
|
|
1166
|
+
period_end: "ISO datetime - resolved window end",
|
|
1167
|
+
messages_count: "number - Sum of messages_count across plan_usages rows for the org in the period",
|
|
1168
|
+
tokens_count: "number - Sum of tokens_count (input + output)",
|
|
1169
|
+
input_tokens_count: "number",
|
|
1170
|
+
output_tokens_count: "number",
|
|
1171
|
+
cost_usd: "string - Decimal as string (e.g. '12.345600')",
|
|
1172
|
+
},
|
|
1173
|
+
note: "Auth requires the caller to be an active OrganizationMember of the path's org. Returns a zero row when the org had no activity in the period, so callers can always render a header without a 404 fallback. Read-only — does not consume usage.",
|
|
1029
1174
|
}
|
|
1030
1175
|
]
|
|
1031
1176
|
},
|
|
@@ -1434,7 +1579,7 @@ const END_USER_ENDPOINTS = {
|
|
|
1434
1579
|
method: "GET",
|
|
1435
1580
|
path: "/v1/entity/schemas",
|
|
1436
1581
|
summary: "List all entity schemas in the organization",
|
|
1437
|
-
auth: { api_key: "sk_live_*", jwt:
|
|
1582
|
+
auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
|
|
1438
1583
|
query_params: {
|
|
1439
1584
|
page: "number (default 1)",
|
|
1440
1585
|
size: "number (default 20, max 100)"
|
|
@@ -1444,26 +1589,49 @@ const END_USER_ENDPOINTS = {
|
|
|
1444
1589
|
method: "POST",
|
|
1445
1590
|
path: "/v1/entity/schemas",
|
|
1446
1591
|
summary: "Create a new entity schema",
|
|
1447
|
-
auth: { api_key: "sk_live_*", jwt:
|
|
1592
|
+
auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
|
|
1448
1593
|
request_body: {
|
|
1449
1594
|
name: "string (required) - unique per organization",
|
|
1450
1595
|
slug: "string (required) - unique per organization, lowercase/hyphens only",
|
|
1451
1596
|
description: "string (optional)",
|
|
1452
|
-
fields_definition:
|
|
1597
|
+
fields_definition: `object (required) - keys are field names (regex ^[a-zA-Z_][a-zA-Z0-9_]*$, max 64 chars; reserved: id, created_at, updated_at, tenant_id), values are objects with at least { "type": <one-of-the-supported-types>, "required": boolean }. Per-type shapes below.`
|
|
1598
|
+
},
|
|
1599
|
+
fields_definition_per_type: {
|
|
1600
|
+
string: '{"type":"string","required":true,"max_length":255,"pattern":"^[a-z]+$"} // pattern is optional regex',
|
|
1601
|
+
text: '{"type":"text","required":false,"max_length":5000}',
|
|
1602
|
+
integer: '{"type":"integer","required":true,"min":0,"max":100}',
|
|
1603
|
+
float: '{"type":"float","required":false,"min":0.0,"max":1.0}',
|
|
1604
|
+
boolean: '{"type":"boolean","required":true}',
|
|
1605
|
+
datetime: '{"type":"datetime","required":false}',
|
|
1606
|
+
enum: '{"type":"enum","required":true,"values":["draft","published","archived"]} // KEY IS "values", NOT "choices" — Genlobe rejects "choices" with 400.',
|
|
1607
|
+
email: '{"type":"email","required":true,"max_length":255}',
|
|
1608
|
+
url: '{"type":"url","required":false,"max_length":2048}',
|
|
1609
|
+
phone: '{"type":"phone","required":false,"max_length":20}'
|
|
1453
1610
|
},
|
|
1454
|
-
|
|
1611
|
+
example_request: `{
|
|
1612
|
+
"name": "Blog Post",
|
|
1613
|
+
"slug": "blog-post",
|
|
1614
|
+
"description": "Journal / blog content",
|
|
1615
|
+
"fields_definition": {
|
|
1616
|
+
"title": {"type": "string", "required": true, "max_length": 255},
|
|
1617
|
+
"body": {"type": "text", "required": true},
|
|
1618
|
+
"status": {"type": "enum", "required": true, "values": ["draft", "published", "archived"]},
|
|
1619
|
+
"views": {"type": "integer", "required": false, "min": 0}
|
|
1620
|
+
}
|
|
1621
|
+
}`,
|
|
1622
|
+
notes: "Returns 409 if name or slug already exists. Common pitfall: the enum field uses 'values' (an array) — 'choices' is rejected with a 400 that names this directly. The validator runs at field level, so all errors come back together in `detail.errors[]`."
|
|
1455
1623
|
},
|
|
1456
1624
|
{
|
|
1457
1625
|
method: "GET",
|
|
1458
1626
|
path: "/v1/entity/schemas/{schema_id}",
|
|
1459
1627
|
summary: "Get a specific schema by ID",
|
|
1460
|
-
auth: { api_key: "sk_live_*", jwt:
|
|
1628
|
+
auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" }
|
|
1461
1629
|
},
|
|
1462
1630
|
{
|
|
1463
1631
|
method: "PUT",
|
|
1464
1632
|
path: "/v1/entity/schemas/{schema_id}",
|
|
1465
1633
|
summary: "Update an existing schema",
|
|
1466
|
-
auth: { api_key: "sk_live_*", jwt:
|
|
1634
|
+
auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
|
|
1467
1635
|
request_body: {
|
|
1468
1636
|
name: "string (optional)",
|
|
1469
1637
|
slug: "string (optional)",
|
|
@@ -1476,47 +1644,47 @@ const END_USER_ENDPOINTS = {
|
|
|
1476
1644
|
method: "DELETE",
|
|
1477
1645
|
path: "/v1/entity/schemas/{schema_id}",
|
|
1478
1646
|
summary: "Delete a schema (may fail if records exist)",
|
|
1479
|
-
auth: { api_key: "sk_live_*", jwt:
|
|
1647
|
+
auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" }
|
|
1480
1648
|
},
|
|
1481
|
-
// ──
|
|
1649
|
+
// ── Entity Record Endpoints (tenant-scoped or user-scoped) ──
|
|
1482
1650
|
{
|
|
1483
1651
|
method: "POST",
|
|
1484
1652
|
path: "/v1/entity/records",
|
|
1485
1653
|
summary: "Create a new entity record",
|
|
1486
|
-
auth: { api_key: "sk_live_* or pk_live_*", jwt:
|
|
1654
|
+
auth: { api_key: "sk_live_* or pk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
|
|
1487
1655
|
request_body: {
|
|
1488
1656
|
schema_id: "uuid (required)",
|
|
1489
1657
|
data: "object (required) - validated against schema fields_definition"
|
|
1490
1658
|
},
|
|
1491
|
-
notes: "
|
|
1659
|
+
notes: "Two modes: (1) sk_live_* without JWT -> tenant-scoped record, created_by_id is NULL (e.g. catalog/blog/global config). (2) API key + end-user JWT -> user-scoped record, created_by_id = user.id. The endpoint accepts both shapes; ownership is determined by whether a JWT is present."
|
|
1492
1660
|
},
|
|
1493
1661
|
{
|
|
1494
1662
|
method: "GET",
|
|
1495
1663
|
path: "/v1/entity/records/{record_id}",
|
|
1496
1664
|
summary: "Get a specific record by ID",
|
|
1497
|
-
auth: { api_key: "sk_live_* or pk_live_*", jwt:
|
|
1665
|
+
auth: { api_key: "sk_live_* or pk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" }
|
|
1498
1666
|
},
|
|
1499
1667
|
{
|
|
1500
1668
|
method: "PUT",
|
|
1501
1669
|
path: "/v1/entity/records/{record_id}",
|
|
1502
1670
|
summary: "Update a record",
|
|
1503
|
-
auth: { api_key: "sk_live_* or pk_live_*", jwt:
|
|
1671
|
+
auth: { api_key: "sk_live_* or pk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
|
|
1504
1672
|
request_body: {
|
|
1505
1673
|
data: "object (required) - validated against schema fields_definition"
|
|
1506
1674
|
},
|
|
1507
|
-
notes: "
|
|
1675
|
+
notes: "updated_by_id follows the same rule as create: NULL for tenant-scoped (sk_live_* alone) or user.id for user-scoped (API key + JWT)."
|
|
1508
1676
|
},
|
|
1509
1677
|
{
|
|
1510
1678
|
method: "DELETE",
|
|
1511
1679
|
path: "/v1/entity/records/{record_id}",
|
|
1512
1680
|
summary: "Delete a record",
|
|
1513
|
-
auth: { api_key: "sk_live_* or pk_live_*", jwt:
|
|
1681
|
+
auth: { api_key: "sk_live_* or pk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" }
|
|
1514
1682
|
},
|
|
1515
1683
|
{
|
|
1516
1684
|
method: "POST",
|
|
1517
1685
|
path: "/v1/entity/records/search",
|
|
1518
1686
|
summary: "Advanced search for entity records with filters and ordering",
|
|
1519
|
-
auth: { api_key: "sk_live_*", jwt:
|
|
1687
|
+
auth: { api_key: "sk_live_* or pk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
|
|
1520
1688
|
request_body: {
|
|
1521
1689
|
schema_id: "uuid (optional) - filter by schema",
|
|
1522
1690
|
query: "object (optional) - filters: {field: value} for equality, {field: {operator: 'gt', value: 100}} for operators"
|
|
@@ -2570,6 +2738,16 @@ for those.
|
|
|
2570
2738
|
session token: keep it in memory or HTTP-only cookies, never in
|
|
2571
2739
|
localStorage if XSS is a real threat for your app.
|
|
2572
2740
|
|
|
2741
|
+
## ⚠️ For agents: don't invent end-user credentials
|
|
2742
|
+
|
|
2743
|
+
If your task requires acting as an end-user (calling a \`jwt: true\`
|
|
2744
|
+
endpoint) and the human has not given you an email + password, **stop
|
|
2745
|
+
and ask**. Do not register a new end-user with an email you generated
|
|
2746
|
+
(\`admin@<their-domain>\`, \`seed@<their-domain>\`, etc.) — invented
|
|
2747
|
+
addresses bounce, AWS SES suppresses the domain at the account level,
|
|
2748
|
+
and the tenant loses email delivery to that destination. See
|
|
2749
|
+
\`get_authentication_flow\` for the full guardrail.
|
|
2750
|
+
|
|
2573
2751
|
## Recommended next step
|
|
2574
2752
|
|
|
2575
2753
|
Run \`recommend_stack\` with the kind of app you want to build to get a
|
|
@@ -2642,6 +2820,22 @@ browser app holds for that session.
|
|
|
2642
2820
|
Refuse — the key belongs in env.
|
|
2643
2821
|
- Logging \`console.log(headers)\` in middleware on a public-facing app.
|
|
2644
2822
|
|
|
2823
|
+
## ⚠️ For agents: don't invent end-user credentials
|
|
2824
|
+
|
|
2825
|
+
A secret key gives you tenant-admin reach, but it does **not** give you
|
|
2826
|
+
the right to fabricate end-user accounts. If a task requires acting as
|
|
2827
|
+
an end-user (calling a \`jwt: true\` endpoint) and the human has not
|
|
2828
|
+
given you an email + password, **stop and ask** them to either share
|
|
2829
|
+
existing credentials or create an end-user in the dashboard. Inventing
|
|
2830
|
+
an email (\`admin@<their-domain>\`, \`seed@<their-domain>\`, etc.) leads
|
|
2831
|
+
to bounces, account-level SES suppression, and email delivery being
|
|
2832
|
+
locked out for that destination — see incident #168 for the canonical
|
|
2833
|
+
example. \`get_authentication_flow\` has the full guardrail.
|
|
2834
|
+
|
|
2835
|
+
Note also: \`/v1/entity/schemas\` is **not** a \`jwt: true\` endpoint —
|
|
2836
|
+
it accepts \`sk_live_*\` alone. Don't register an end-user just to call
|
|
2837
|
+
it. Read \`get_endpoint_details\` first.
|
|
2838
|
+
|
|
2645
2839
|
## Recommended next step
|
|
2646
2840
|
|
|
2647
2841
|
Run \`recommend_stack\` with your app_type to get a server-aware framework
|
|
@@ -3445,6 +3639,17 @@ Use search_endpoints tool to find available endpoints.`,
|
|
|
3445
3639
|
type: "text",
|
|
3446
3640
|
text: `# Authentication Flow Guide
|
|
3447
3641
|
|
|
3642
|
+
> ## ⚠️ When you (the agent) need an end-user JWT
|
|
3643
|
+
>
|
|
3644
|
+
> Some endpoints document themselves as \`jwt: true\` (e.g. some \`/v1/entity/records\` modes, profile updates, anything user-scoped). If your task requires calling one of these and you do **not** have an end-user email and password supplied by the human you are working with:
|
|
3645
|
+
>
|
|
3646
|
+
> 1. **Stop.** Ask the human for credentials of an existing end-user, or ask them to create one in the dashboard and give you the credentials.
|
|
3647
|
+
> 2. **Do NOT register a new end-user with an email you generated.** Inventing an email (e.g. \`admin@<their-domain>\`, \`seed@<their-domain>\`) leads to bounces. AWS SES then suppresses the address at the account level and locks the tenant out of email delivery for that destination — recovery requires manual operator action. This is exactly how production incident #168 happened.
|
|
3648
|
+
> 3. **Schemas don't need a JWT.** \`/v1/entity/schemas\` is a tenant-admin operation: \`sk_live_*\` alone is enough (see issue #177). Read \`get_endpoint_details\` for the schemas endpoint before assuming you need to register a user.
|
|
3649
|
+
> 4. If the human says \"register a user with this email: ...\" — fine, that's an explicit instruction. Inventing one is not.
|
|
3650
|
+
|
|
3651
|
+
---
|
|
3652
|
+
|
|
3448
3653
|
## 1. Registration Flow
|
|
3449
3654
|
|
|
3450
3655
|
\`\`\`typescript
|
package/package.json
CHANGED