@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.
- package/README.md +0 -29
- package/dist/index.js +76 -16
- 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: "
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
1502
|
+
auth: { api_key: "sk_live_*", jwt: false, headers: "X-Organization-Id required when key is not organization-scoped" }
|
|
1480
1503
|
},
|
|
1481
|
-
// ──
|
|
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:
|
|
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: "
|
|
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:
|
|
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:
|
|
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: "
|
|
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:
|
|
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:
|
|
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