@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 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
- # Database Schema Server
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
@@ -6,7 +6,7 @@ services:
6
6
  POSTGRES_PASSWORD: emseepea
7
7
  POSTGRES_USER: emseepea
8
8
  healthcheck:
9
- test: ["CMD-SHELL", "pg_isready -U emseepea -d emseepea"]
9
+ test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U emseepea -d emseepea"]
10
10
  interval: 1s
11
11
  timeout: 3s
12
12
  retries: 20
@@ -19,7 +19,7 @@
19
19
  "devDependencies": {
20
20
  "kanel": "4.0.4",
21
21
  "kanel-zod": "4.0.1",
22
- "@emseepea/testing": "0.5.2",
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.3.3",
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 { createEmseepea, discoverCapabilities } from "@emseepea/server";
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,5 +1,7 @@
1
1
  import type { Pool } from "pg";
2
+ import type { AccessPolicy } from "@emseepea/server";
2
3
 
3
4
  export interface DatabaseSchemaContext {
4
5
  readonly database: () => Pool | undefined;
6
+ readonly access: AccessPolicy;
5
7
  }
@@ -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: "public",
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: "public",
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: "public",
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 { startMcpServer } from "@emseepea/testing";
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.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.3.3",
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.5.3",
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
- "access": "public",
52
- "provenance": true
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
  }