@genlobe/mcp-server 3.1.1 → 3.1.8

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 (3) hide show
  1. package/README.md +0 -29
  2. package/dist/index.js +76 -16
  3. 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
@@ -271,7 +271,7 @@ const END_USER_ENDPOINTS = {
271
271
  email: "string (required) - valid email address"
272
272
  },
273
273
  response: { message: "Verification email sent" },
274
- note: "Always returns success even if email doesn't exist (security). Rate limited to prevent abuse."
274
+ note: "Returns 200 with a generic 'will be sent if the user exists' message when the user does not exist (security). Returns 400 with `{ detail: \"<reason>\" }` when delivery cannot proceed — the detail mirrors the underlying provider error, e.g. `\"destination is suppressed: BOUNCE\"` or `\"destination is suppressed: COMPLAINT\"` when AWS SES has the destination on its account suppression list. The email cannot be delivered until the destination's MX/inbox is fixed by its owner, or an admin force-verifies the user from the tenant dashboard. Rate limited to prevent abuse."
275
275
  },
276
276
  {
277
277
  method: "GET",
@@ -1434,7 +1434,7 @@ const END_USER_ENDPOINTS = {
1434
1434
  method: "GET",
1435
1435
  path: "/v1/entity/schemas",
1436
1436
  summary: "List all entity schemas in the organization",
1437
- auth: { api_key: "sk_live_*", jwt: true, headers: "X-Organization-Id required" },
1437
+ auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
1438
1438
  query_params: {
1439
1439
  page: "number (default 1)",
1440
1440
  size: "number (default 20, max 100)"
@@ -1444,26 +1444,49 @@ const END_USER_ENDPOINTS = {
1444
1444
  method: "POST",
1445
1445
  path: "/v1/entity/schemas",
1446
1446
  summary: "Create a new entity schema",
1447
- auth: { api_key: "sk_live_*", jwt: true, headers: "X-Organization-Id required" },
1447
+ auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
1448
1448
  request_body: {
1449
1449
  name: "string (required) - unique per organization",
1450
1450
  slug: "string (required) - unique per organization, lowercase/hyphens only",
1451
1451
  description: "string (optional)",
1452
- fields_definition: "object (required) - field definitions with type, required, constraints"
1452
+ 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.`
1453
1453
  },
1454
- notes: "Supported field types: string, integer, float, boolean, datetime, text, enum, email, url, phone. Returns 409 if name or slug already exists."
1454
+ fields_definition_per_type: {
1455
+ string: '{"type":"string","required":true,"max_length":255,"pattern":"^[a-z]+$"} // pattern is optional regex',
1456
+ text: '{"type":"text","required":false,"max_length":5000}',
1457
+ integer: '{"type":"integer","required":true,"min":0,"max":100}',
1458
+ float: '{"type":"float","required":false,"min":0.0,"max":1.0}',
1459
+ boolean: '{"type":"boolean","required":true}',
1460
+ datetime: '{"type":"datetime","required":false}',
1461
+ enum: '{"type":"enum","required":true,"values":["draft","published","archived"]} // KEY IS "values", NOT "choices" — Genlobe rejects "choices" with 400.',
1462
+ email: '{"type":"email","required":true,"max_length":255}',
1463
+ url: '{"type":"url","required":false,"max_length":2048}',
1464
+ phone: '{"type":"phone","required":false,"max_length":20}'
1465
+ },
1466
+ example_request: `{
1467
+ "name": "Blog Post",
1468
+ "slug": "blog-post",
1469
+ "description": "Journal / blog content",
1470
+ "fields_definition": {
1471
+ "title": {"type": "string", "required": true, "max_length": 255},
1472
+ "body": {"type": "text", "required": true},
1473
+ "status": {"type": "enum", "required": true, "values": ["draft", "published", "archived"]},
1474
+ "views": {"type": "integer", "required": false, "min": 0}
1475
+ }
1476
+ }`,
1477
+ 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
1478
  },
1456
1479
  {
1457
1480
  method: "GET",
1458
1481
  path: "/v1/entity/schemas/{schema_id}",
1459
1482
  summary: "Get a specific schema by ID",
1460
- auth: { api_key: "sk_live_*", jwt: true, headers: "X-Organization-Id required" }
1483
+ auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" }
1461
1484
  },
1462
1485
  {
1463
1486
  method: "PUT",
1464
1487
  path: "/v1/entity/schemas/{schema_id}",
1465
1488
  summary: "Update an existing schema",
1466
- auth: { api_key: "sk_live_*", jwt: true, headers: "X-Organization-Id required" },
1489
+ auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
1467
1490
  request_body: {
1468
1491
  name: "string (optional)",
1469
1492
  slug: "string (optional)",
@@ -1476,47 +1499,47 @@ const END_USER_ENDPOINTS = {
1476
1499
  method: "DELETE",
1477
1500
  path: "/v1/entity/schemas/{schema_id}",
1478
1501
  summary: "Delete a schema (may fail if records exist)",
1479
- auth: { api_key: "sk_live_*", jwt: true, headers: "X-Organization-Id required" }
1502
+ auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" }
1480
1503
  },
1481
- // ── End-User Record Endpoints ──
1504
+ // ── Entity Record Endpoints (tenant-scoped or user-scoped) ──
1482
1505
  {
1483
1506
  method: "POST",
1484
1507
  path: "/v1/entity/records",
1485
1508
  summary: "Create a new entity record",
1486
- auth: { api_key: "sk_live_* or pk_live_*", jwt: true, headers: "X-Organization-Id required" },
1509
+ auth: { api_key: "sk_live_* or pk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
1487
1510
  request_body: {
1488
1511
  schema_id: "uuid (required)",
1489
1512
  data: "object (required) - validated against schema fields_definition"
1490
1513
  },
1491
- notes: "Sets created_by_id to the authenticated end-user."
1514
+ 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
1515
  },
1493
1516
  {
1494
1517
  method: "GET",
1495
1518
  path: "/v1/entity/records/{record_id}",
1496
1519
  summary: "Get a specific record by ID",
1497
- auth: { api_key: "sk_live_* or pk_live_*", jwt: true, headers: "X-Organization-Id required" }
1520
+ auth: { api_key: "sk_live_* or pk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" }
1498
1521
  },
1499
1522
  {
1500
1523
  method: "PUT",
1501
1524
  path: "/v1/entity/records/{record_id}",
1502
1525
  summary: "Update a record",
1503
- auth: { api_key: "sk_live_* or pk_live_*", jwt: true, headers: "X-Organization-Id required" },
1526
+ auth: { api_key: "sk_live_* or pk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
1504
1527
  request_body: {
1505
1528
  data: "object (required) - validated against schema fields_definition"
1506
1529
  },
1507
- notes: "Sets updated_by_id to the authenticated end-user."
1530
+ 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
1531
  },
1509
1532
  {
1510
1533
  method: "DELETE",
1511
1534
  path: "/v1/entity/records/{record_id}",
1512
1535
  summary: "Delete a record",
1513
- auth: { api_key: "sk_live_* or pk_live_*", jwt: true, headers: "X-Organization-Id required" }
1536
+ auth: { api_key: "sk_live_* or pk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" }
1514
1537
  },
1515
1538
  {
1516
1539
  method: "POST",
1517
1540
  path: "/v1/entity/records/search",
1518
1541
  summary: "Advanced search for entity records with filters and ordering",
1519
- auth: { api_key: "sk_live_*", jwt: true, headers: "X-Organization-Id required" },
1542
+ auth: { api_key: "sk_live_* or pk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" },
1520
1543
  request_body: {
1521
1544
  schema_id: "uuid (optional) - filter by schema",
1522
1545
  query: "object (optional) - filters: {field: value} for equality, {field: {operator: 'gt', value: 100}} for operators"
@@ -2570,6 +2593,16 @@ for those.
2570
2593
  session token: keep it in memory or HTTP-only cookies, never in
2571
2594
  localStorage if XSS is a real threat for your app.
2572
2595
 
2596
+ ## ⚠️ For agents: don't invent end-user credentials
2597
+
2598
+ If your task requires acting as an end-user (calling a \`jwt: true\`
2599
+ endpoint) and the human has not given you an email + password, **stop
2600
+ and ask**. Do not register a new end-user with an email you generated
2601
+ (\`admin@<their-domain>\`, \`seed@<their-domain>\`, etc.) — invented
2602
+ addresses bounce, AWS SES suppresses the domain at the account level,
2603
+ and the tenant loses email delivery to that destination. See
2604
+ \`get_authentication_flow\` for the full guardrail.
2605
+
2573
2606
  ## Recommended next step
2574
2607
 
2575
2608
  Run \`recommend_stack\` with the kind of app you want to build to get a
@@ -2642,6 +2675,22 @@ browser app holds for that session.
2642
2675
  Refuse — the key belongs in env.
2643
2676
  - Logging \`console.log(headers)\` in middleware on a public-facing app.
2644
2677
 
2678
+ ## ⚠️ For agents: don't invent end-user credentials
2679
+
2680
+ A secret key gives you tenant-admin reach, but it does **not** give you
2681
+ the right to fabricate end-user accounts. If a task requires acting as
2682
+ an end-user (calling a \`jwt: true\` endpoint) and the human has not
2683
+ given you an email + password, **stop and ask** them to either share
2684
+ existing credentials or create an end-user in the dashboard. Inventing
2685
+ an email (\`admin@<their-domain>\`, \`seed@<their-domain>\`, etc.) leads
2686
+ to bounces, account-level SES suppression, and email delivery being
2687
+ locked out for that destination — see incident #168 for the canonical
2688
+ example. \`get_authentication_flow\` has the full guardrail.
2689
+
2690
+ Note also: \`/v1/entity/schemas\` is **not** a \`jwt: true\` endpoint —
2691
+ it accepts \`sk_live_*\` alone. Don't register an end-user just to call
2692
+ it. Read \`get_endpoint_details\` first.
2693
+
2645
2694
  ## Recommended next step
2646
2695
 
2647
2696
  Run \`recommend_stack\` with your app_type to get a server-aware framework
@@ -3445,6 +3494,17 @@ Use search_endpoints tool to find available endpoints.`,
3445
3494
  type: "text",
3446
3495
  text: `# Authentication Flow Guide
3447
3496
 
3497
+ > ## ⚠️ When you (the agent) need an end-user JWT
3498
+ >
3499
+ > 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:
3500
+ >
3501
+ > 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.
3502
+ > 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.
3503
+ > 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.
3504
+ > 4. If the human says \"register a user with this email: ...\" — fine, that's an explicit instruction. Inventing one is not.
3505
+
3506
+ ---
3507
+
3448
3508
  ## 1. Registration Flow
3449
3509
 
3450
3510
  \`\`\`typescript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genlobe/mcp-server",
3
- "version": "3.1.1",
3
+ "version": "3.1.8",
4
4
  "description": "MCP Server for GenLobe SaaS API - Provides AI assistants with comprehensive API documentation for building frontend applications",
5
5
  "main": "dist/index.js",
6
6
  "bin": {