@emseepea/create-database-schema-server 0.0.0 → 0.0.2
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 +24 -0
- package/initializer-dist/template/README.md +25 -1
- package/initializer-dist/template/compose.yaml +1 -1
- package/initializer-dist/template/package.json +2 -2
- package/initializer-dist/template/src/app.ts +12 -2
- package/initializer-dist/template/src/capabilities/context.ts +2 -0
- package/initializer-dist/template/src/capabilities/tool.add-pea-variety.ts +3 -3
- package/initializer-dist/template/src/capabilities/tool.list-pea-varieties.ts +3 -3
- package/initializer-dist/template/src/capabilities/tool.summarize-pea-catalog.ts +3 -3
- package/initializer-dist/template/test/server.test.mjs +22 -1
- package/package.json +8 -23
package/README.md
CHANGED
|
@@ -28,6 +28,30 @@ Compose setup, lint checks, ordinary tests, and semantic tests.
|
|
|
28
28
|
|
|
29
29
|
<!-- generated-project-readme -->
|
|
30
30
|
|
|
31
|
+
## Choose Open or Protected Access
|
|
32
|
+
|
|
33
|
+
Start open when the catalogue and operations are public.
|
|
34
|
+
|
|
35
|
+
To protect this template, pass both options to the app factory:
|
|
36
|
+
|
|
37
|
+
- `access: { access: "protected", requiredScopes: ["peas:read"] }`
|
|
38
|
+
- an `authentication` adapter
|
|
39
|
+
|
|
40
|
+
Keep `authentication.discovery` as `"public"` unless capability names or
|
|
41
|
+
schemas are sensitive. Use `"protected"` only when each principal should see a
|
|
42
|
+
permission-filtered catalogue. OAuth metadata remains public in both modes.
|
|
43
|
+
|
|
44
|
+
## Add Observability
|
|
45
|
+
|
|
46
|
+
The same factory accepts `observability`.
|
|
47
|
+
|
|
48
|
+
- Use `structuredLogging` for safe structured events.
|
|
49
|
+
- Use `openTelemetry` for traces and metrics.
|
|
50
|
+
|
|
51
|
+
Adapters receive only redacted framework events. They never receive request
|
|
52
|
+
bodies, arguments, results, tokens, provider claims, or raw errors. See the
|
|
53
|
+
[server API](https://github.com/emseepea/emseepea/tree/main/packages/framework#authentication-and-observability) for the complete configuration.
|
|
54
|
+
|
|
31
55
|
## Database Schema Server
|
|
32
56
|
|
|
33
57
|
PostgreSQL is authoritative for the internal contract. Kanel and Kanel Zod
|
|
@@ -1,4 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Choose Open or Protected Access
|
|
2
|
+
|
|
3
|
+
Start open when the catalogue and operations are public.
|
|
4
|
+
|
|
5
|
+
To protect this template, pass both options to the app factory:
|
|
6
|
+
|
|
7
|
+
- `access: { access: "protected", requiredScopes: ["peas:read"] }`
|
|
8
|
+
- an `authentication` adapter
|
|
9
|
+
|
|
10
|
+
Keep `authentication.discovery` as `"public"` unless capability names or
|
|
11
|
+
schemas are sensitive. Use `"protected"` only when each principal should see a
|
|
12
|
+
permission-filtered catalogue. OAuth metadata remains public in both modes.
|
|
13
|
+
|
|
14
|
+
## Add Observability
|
|
15
|
+
|
|
16
|
+
The same factory accepts `observability`.
|
|
17
|
+
|
|
18
|
+
- Use `structuredLogging` for safe structured events.
|
|
19
|
+
- Use `openTelemetry` for traces and metrics.
|
|
20
|
+
|
|
21
|
+
Adapters receive only redacted framework events. They never receive request
|
|
22
|
+
bodies, arguments, results, tokens, provider claims, or raw errors. See the
|
|
23
|
+
[server API](https://github.com/emseepea/emseepea/tree/main/packages/framework#authentication-and-observability) for the complete configuration.
|
|
24
|
+
|
|
25
|
+
## Database Schema Server
|
|
2
26
|
|
|
3
27
|
PostgreSQL is authoritative for the internal contract. Kanel and Kanel Zod
|
|
4
28
|
generate TypeScript and runtime validators from the committed schema. Normal
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"kanel": "4.0.4",
|
|
21
21
|
"kanel-zod": "4.0.1",
|
|
22
|
-
"@emseepea/testing": "0.
|
|
22
|
+
"@emseepea/testing": "0.6.1",
|
|
23
23
|
"@modelcontextprotocol/client": "2.0.0",
|
|
24
24
|
"@types/node": "24.13.3",
|
|
25
25
|
"@types/pg": "8.23.1",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"private": true,
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@emseepea/server": "0.
|
|
34
|
+
"@emseepea/server": "0.4.0",
|
|
35
35
|
"pg": "8.23.0",
|
|
36
36
|
"zod": "4.4.3"
|
|
37
37
|
}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createEmseepea,
|
|
3
|
+
discoverCapabilities,
|
|
4
|
+
type AccessPolicy,
|
|
5
|
+
type EmseepeaExtensions,
|
|
6
|
+
} from "@emseepea/server";
|
|
2
7
|
import { Pool } from "pg";
|
|
3
8
|
import { z } from "zod";
|
|
4
9
|
|
|
5
|
-
export interface DatabaseSchemaExampleOptions {
|
|
10
|
+
export interface DatabaseSchemaExampleOptions extends EmseepeaExtensions {
|
|
6
11
|
readonly databaseUrl: string;
|
|
12
|
+
readonly access?: AccessPolicy;
|
|
7
13
|
}
|
|
8
14
|
|
|
9
15
|
export async function createDatabaseSchemaExample(options: DatabaseSchemaExampleOptions) {
|
|
16
|
+
const { access = { access: "public" }, authentication, observability } = options;
|
|
10
17
|
const databaseUrl = z.string().url().refine(
|
|
11
18
|
(value) => ["postgres:", "postgresql:"].includes(new URL(value).protocol),
|
|
12
19
|
"databaseUrl must use PostgreSQL",
|
|
@@ -41,7 +48,10 @@ export async function createDatabaseSchemaExample(options: DatabaseSchemaExample
|
|
|
41
48
|
readinessTimeoutMs: 2_500,
|
|
42
49
|
...await discoverCapabilities(new URL("./capabilities/", import.meta.url), {
|
|
43
50
|
database: () => database,
|
|
51
|
+
access,
|
|
44
52
|
}),
|
|
53
|
+
authentication,
|
|
54
|
+
observability,
|
|
45
55
|
});
|
|
46
56
|
const closeProvider = async () => {
|
|
47
57
|
const activeDatabase = database;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineTool, type CapabilityModuleFactory } from "@emseepea/server";
|
|
1
|
+
import { defineTool, type CapabilityModuleFactory, type ToolContext } from "@emseepea/server";
|
|
2
2
|
import { peaVarietyCatalog } from "../generated/public/PeaVarietyCatalog.mjs";
|
|
3
3
|
import { varietySchema } from "../pea-variety.js";
|
|
4
4
|
import type { DatabaseSchemaContext } from "./context.js";
|
|
@@ -8,11 +8,11 @@ const outputSchema = varietySchema;
|
|
|
8
8
|
|
|
9
9
|
export default ((context) => defineTool({
|
|
10
10
|
name: "add-pea-variety",
|
|
11
|
-
access
|
|
11
|
+
...context.access,
|
|
12
12
|
description: "Add one pea variety to the catalogue.",
|
|
13
13
|
inputSchema,
|
|
14
14
|
outputSchema,
|
|
15
|
-
async handler(variety, { signal }) {
|
|
15
|
+
async handler(variety, { signal }: ToolContext) {
|
|
16
16
|
const database = context.database();
|
|
17
17
|
if (!database) throw new Error("Variety provider unavailable");
|
|
18
18
|
signal.throwIfAborted();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineTool, type CapabilityModuleFactory } from "@emseepea/server";
|
|
1
|
+
import { defineTool, type CapabilityModuleFactory, type ToolContext } from "@emseepea/server";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { peaVarietyCatalog } from "../generated/public/PeaVarietyCatalog.mjs";
|
|
4
4
|
import { varietySchema } from "../pea-variety.js";
|
|
@@ -14,11 +14,11 @@ const outputSchema = z.object({
|
|
|
14
14
|
|
|
15
15
|
export default ((context) => defineTool({
|
|
16
16
|
name: "list-pea-varieties",
|
|
17
|
-
access
|
|
17
|
+
...context.access,
|
|
18
18
|
description: "List pea varieties, optionally filtered by pea type.",
|
|
19
19
|
inputSchema,
|
|
20
20
|
outputSchema,
|
|
21
|
-
async handler({ pea_type }, { signal }) {
|
|
21
|
+
async handler({ pea_type }, { signal }: ToolContext) {
|
|
22
22
|
const database = context.database();
|
|
23
23
|
if (!database) throw new Error("Variety provider unavailable");
|
|
24
24
|
signal.throwIfAborted();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineTool, type CapabilityModuleFactory } from "@emseepea/server";
|
|
1
|
+
import { defineTool, type CapabilityModuleFactory, type ToolContext } from "@emseepea/server";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { peaCatalogSummary } from "../generated/public/PeaCatalogSummary.mjs";
|
|
4
4
|
import type { DatabaseSchemaContext } from "./context.js";
|
|
@@ -16,11 +16,11 @@ const outputSchema = z.object({
|
|
|
16
16
|
|
|
17
17
|
export default ((context) => defineTool({
|
|
18
18
|
name: "summarize-pea-catalog",
|
|
19
|
-
access
|
|
19
|
+
...context.access,
|
|
20
20
|
description: "Summarize the pea catalogue.",
|
|
21
21
|
inputSchema,
|
|
22
22
|
outputSchema,
|
|
23
|
-
async handler({ pea_type }, { signal }) {
|
|
23
|
+
async handler({ pea_type }, { signal }: ToolContext) {
|
|
24
24
|
const database = context.database();
|
|
25
25
|
if (!database) throw new Error("Variety provider unavailable");
|
|
26
26
|
signal.throwIfAborted();
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test, { after } from "node:test";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
insecureTestAuthentication,
|
|
6
|
+
startEmseepea,
|
|
7
|
+
startMcpServer,
|
|
8
|
+
} from "@emseepea/testing";
|
|
5
9
|
import { Pool } from "pg";
|
|
10
|
+
import { createDatabaseSchemaExample } from "../dist/app.js";
|
|
6
11
|
|
|
7
12
|
const databaseUrl = process.env.DATABASE_URL;
|
|
8
13
|
assert.ok(databaseUrl, "DATABASE_URL is required");
|
|
@@ -74,6 +79,22 @@ test("reads and writes through the view and summarizes through the procedure", a
|
|
|
74
79
|
);
|
|
75
80
|
});
|
|
76
81
|
|
|
82
|
+
test("the same template composes protected access and observability", async (t) => {
|
|
83
|
+
const events = [];
|
|
84
|
+
const permissions = ["catalogue:write"];
|
|
85
|
+
const { app } = await createDatabaseSchemaExample({
|
|
86
|
+
databaseUrl,
|
|
87
|
+
access: { access: "protected", requiredScopes: permissions },
|
|
88
|
+
authentication: insecureTestAuthentication(permissions),
|
|
89
|
+
observability: [{ id: "test-log", emit: (event) => events.push(event) }],
|
|
90
|
+
});
|
|
91
|
+
const running = await startEmseepea(t, app);
|
|
92
|
+
const client = await running.connect("test-token");
|
|
93
|
+
const result = await client.callTool({ name: "list-pea-varieties", arguments: {} });
|
|
94
|
+
assert.equal(result.isError, false);
|
|
95
|
+
assert.ok(events.some(({ capability }) => capability === "list-pea-varieties"));
|
|
96
|
+
});
|
|
97
|
+
|
|
77
98
|
test("the catalogue read has a fixed 20-row boundary", async (t) => {
|
|
78
99
|
await database.query(`
|
|
79
100
|
INSERT INTO pea_variety_catalog
|
package/package.json
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emseepea/create-database-schema-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Create an Em See Pea server with internal validation generated from a PostgreSQL schema.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"starterDependencies": [
|
|
8
|
-
"@emseepea/server",
|
|
9
|
-
"pg",
|
|
10
|
-
"zod"
|
|
11
|
-
],
|
|
7
|
+
"starterDependencies": ["@emseepea/server", "pg", "zod"],
|
|
12
8
|
"scripts": {
|
|
13
9
|
"build": "npm run build:example && npm run build:initializer",
|
|
14
10
|
"build:example": "tsc -p tsconfig.json",
|
|
@@ -25,12 +21,12 @@
|
|
|
25
21
|
"prepack": "npm run build:initializer"
|
|
26
22
|
},
|
|
27
23
|
"devDependencies": {
|
|
28
|
-
"@emseepea/server": "0.
|
|
24
|
+
"@emseepea/server": "0.4.0",
|
|
29
25
|
"pg": "8.23.0",
|
|
30
26
|
"zod": "4.4.3",
|
|
31
27
|
"kanel": "4.0.4",
|
|
32
28
|
"kanel-zod": "4.0.1",
|
|
33
|
-
"@emseepea/testing": "0.
|
|
29
|
+
"@emseepea/testing": "0.6.1",
|
|
34
30
|
"@modelcontextprotocol/client": "2.0.0",
|
|
35
31
|
"@types/node": "24.13.3",
|
|
36
32
|
"@types/pg": "8.23.1",
|
|
@@ -40,21 +36,10 @@
|
|
|
40
36
|
"engines": {
|
|
41
37
|
"node": ">=22.13.0"
|
|
42
38
|
},
|
|
43
|
-
"repository": {
|
|
44
|
-
"type": "git",
|
|
45
|
-
"url": "git+https://github.com/emseepea/emseepea.git",
|
|
46
|
-
"directory": "examples/database-schema-server"
|
|
47
|
-
},
|
|
39
|
+
"repository": { "type": "git", "url": "git+https://github.com/emseepea/emseepea.git", "directory": "examples/database-schema-server" },
|
|
48
40
|
"homepage": "https://emseepea.github.io/emseepea/examples/",
|
|
49
41
|
"bugs": "https://github.com/emseepea/emseepea/issues",
|
|
50
|
-
"publishConfig": {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
54
|
-
"bin": {
|
|
55
|
-
"create-database-schema-server": "./initializer-dist/create.mjs"
|
|
56
|
-
},
|
|
57
|
-
"files": [
|
|
58
|
-
"initializer-dist"
|
|
59
|
-
]
|
|
42
|
+
"publishConfig": { "access": "public", "provenance": true },
|
|
43
|
+
"bin": { "create-database-schema-server": "./initializer-dist/create.mjs" },
|
|
44
|
+
"files": ["initializer-dist"]
|
|
60
45
|
}
|