@emseepea/create-mongodb-backed-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/package.json +2 -2
- package/initializer-dist/template/src/app.ts +19 -3
- package/initializer-dist/template/src/capabilities/tool.add-pea-variety.ts +3 -3
- package/initializer-dist/template/src/capabilities/tool.list-pea-observations.ts +3 -3
- package/initializer-dist/template/src/capabilities/tool.list-pea-varieties.ts +3 -3
- package/initializer-dist/template/src/capabilities/tool.record-pea-observation.ts +3 -3
- package/initializer-dist/template/src/database.ts +2 -0
- package/initializer-dist/template/test/server.test.mjs +21 -1
- package/package.json +9 -28
package/README.md
CHANGED
|
@@ -27,6 +27,30 @@ tests.
|
|
|
27
27
|
|
|
28
28
|
<!-- generated-project-readme -->
|
|
29
29
|
|
|
30
|
+
## Choose Open or Protected Access
|
|
31
|
+
|
|
32
|
+
Start open when the catalogue and operations are public.
|
|
33
|
+
|
|
34
|
+
To protect this template, pass both options to the app factory:
|
|
35
|
+
|
|
36
|
+
- `access: { access: "protected", requiredScopes: ["peas:read"] }`
|
|
37
|
+
- an `authentication` adapter
|
|
38
|
+
|
|
39
|
+
Keep `authentication.discovery` as `"public"` unless capability names or
|
|
40
|
+
schemas are sensitive. Use `"protected"` only when each principal should see a
|
|
41
|
+
permission-filtered catalogue. OAuth metadata remains public in both modes.
|
|
42
|
+
|
|
43
|
+
## Add Observability
|
|
44
|
+
|
|
45
|
+
The same factory accepts `observability`.
|
|
46
|
+
|
|
47
|
+
- Use `structuredLogging` for safe structured events.
|
|
48
|
+
- Use `openTelemetry` for traces and metrics.
|
|
49
|
+
|
|
50
|
+
Adapters receive only redacted framework events. They never receive request
|
|
51
|
+
bodies, arguments, results, tokens, provider claims, or raw errors. See the
|
|
52
|
+
[server API](https://github.com/emseepea/emseepea/tree/main/packages/framework#authentication-and-observability) for the complete configuration.
|
|
53
|
+
|
|
30
54
|
## MongoDB-Backed Server
|
|
31
55
|
|
|
32
56
|
`pea_varieties` is schema-enforced. `src/pea-document.ts` contains its only
|
|
@@ -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
|
+
## MongoDB-Backed Server
|
|
2
26
|
|
|
3
27
|
`pea_varieties` is schema-enforced. `src/pea-document.ts` contains its only
|
|
4
28
|
stored-document schema. MongoDB applies that exact object as its collection
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"lint": "oxlint src test eval"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@emseepea/testing": "0.
|
|
19
|
+
"@emseepea/testing": "0.6.1",
|
|
20
20
|
"@types/node": "24.13.3",
|
|
21
21
|
"typescript": "6.0.3",
|
|
22
22
|
"oxlint": "1.80.0"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"private": true,
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@emseepea/server": "0.
|
|
29
|
+
"@emseepea/server": "0.4.0",
|
|
30
30
|
"ajv": "8.20.0",
|
|
31
31
|
"json-schema-to-ts": "3.1.1",
|
|
32
32
|
"mongodb": "7.6.0",
|
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createEmseepea,
|
|
3
|
+
discoverCapabilities,
|
|
4
|
+
type AccessPolicy,
|
|
5
|
+
type EmseepeaExtensions,
|
|
6
|
+
} from "@emseepea/server";
|
|
2
7
|
import type { Collection, Db } from "mongodb";
|
|
3
8
|
import { z } from "zod";
|
|
4
9
|
import { createDatabase } from "./database.js";
|
|
5
10
|
import type { PeaObservationDocument } from "./pea-observation-document.js";
|
|
6
11
|
import type { PeaDocument } from "./pea-document.js";
|
|
7
12
|
|
|
8
|
-
export interface MongoExampleOptions
|
|
13
|
+
export interface MongoExampleOptions extends EmseepeaExtensions {
|
|
14
|
+
readonly uri: string;
|
|
15
|
+
readonly access?: AccessPolicy;
|
|
16
|
+
}
|
|
9
17
|
|
|
10
|
-
export async function createMongoExample({
|
|
18
|
+
export async function createMongoExample({
|
|
19
|
+
uri,
|
|
20
|
+
access = { access: "public" },
|
|
21
|
+
authentication,
|
|
22
|
+
observability,
|
|
23
|
+
}: MongoExampleOptions) {
|
|
11
24
|
const parsedUri = z.string().url().refine(
|
|
12
25
|
(value) => ["mongodb:", "mongodb+srv:"].includes(new URL(value).protocol),
|
|
13
26
|
"uri must use MongoDB",
|
|
@@ -33,10 +46,13 @@ export async function createMongoExample({ uri }: MongoExampleOptions) {
|
|
|
33
46
|
},
|
|
34
47
|
readinessTimeoutMs: 2_500,
|
|
35
48
|
...await discoverCapabilities(new URL("./capabilities/", import.meta.url), {
|
|
49
|
+
access,
|
|
36
50
|
database: () => database,
|
|
37
51
|
observations: () => observations,
|
|
38
52
|
varieties: () => varieties,
|
|
39
53
|
}),
|
|
54
|
+
authentication,
|
|
55
|
+
observability,
|
|
40
56
|
});
|
|
41
57
|
const closeProvider = async () => {
|
|
42
58
|
observations = undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { defineTool, type CapabilityModuleFactory } from "@emseepea/server";
|
|
2
|
+
import { defineTool, type CapabilityModuleFactory, type ToolContext } from "@emseepea/server";
|
|
3
3
|
import { parsePeaDocument } from "../pea-document.js";
|
|
4
4
|
import { varietySchema } from "../pea-variety.js";
|
|
5
5
|
import type { MongoContext } from "../database.js";
|
|
@@ -9,11 +9,11 @@ const outputSchema = varietySchema;
|
|
|
9
9
|
|
|
10
10
|
export default ((context) => defineTool({
|
|
11
11
|
name: "add-pea-variety",
|
|
12
|
-
access
|
|
12
|
+
...context.access,
|
|
13
13
|
description: "Add one pea variety to the catalogue.",
|
|
14
14
|
inputSchema,
|
|
15
15
|
outputSchema,
|
|
16
|
-
async handler(variety, { signal }) {
|
|
16
|
+
async handler(variety, { signal }: ToolContext) {
|
|
17
17
|
const collection = context.varieties();
|
|
18
18
|
if (!collection) throw new Error("Variety provider unavailable");
|
|
19
19
|
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 type { MongoContext } from "../database.js";
|
|
4
4
|
import { parsePeaObservationDocument } from "../pea-observation-document.js";
|
|
@@ -15,11 +15,11 @@ const outputSchema = z.object({
|
|
|
15
15
|
|
|
16
16
|
export default ((context) => defineTool({
|
|
17
17
|
name: "list-pea-observations",
|
|
18
|
-
access
|
|
18
|
+
...context.access,
|
|
19
19
|
description: "List up to 20 recent observations of pea plants.",
|
|
20
20
|
inputSchema,
|
|
21
21
|
outputSchema,
|
|
22
|
-
async handler({ variety_name }, { signal }) {
|
|
22
|
+
async handler({ variety_name }, { signal }: ToolContext) {
|
|
23
23
|
const collection = context.observations();
|
|
24
24
|
if (!collection) throw new Error("Observation provider unavailable");
|
|
25
25
|
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 { parsePeaDocument } from "../pea-document.js";
|
|
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 up to 20 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 collection = context.varieties();
|
|
23
23
|
if (!collection) throw new Error("Variety provider unavailable");
|
|
24
24
|
signal.throwIfAborted();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { defineTool, type CapabilityModuleFactory } from "@emseepea/server";
|
|
2
|
+
import { defineTool, type CapabilityModuleFactory, type ToolContext } from "@emseepea/server";
|
|
3
3
|
import type { MongoContext } from "../database.js";
|
|
4
4
|
import { parsePeaObservationDocument } from "../pea-observation-document.js";
|
|
5
5
|
import { observationSchema } from "../pea-observation.js";
|
|
@@ -9,11 +9,11 @@ const outputSchema = observationSchema;
|
|
|
9
9
|
|
|
10
10
|
export default ((context) => defineTool({
|
|
11
11
|
name: "record-pea-observation",
|
|
12
|
-
access
|
|
12
|
+
...context.access,
|
|
13
13
|
description: "Record one dated observation of a pea plant.",
|
|
14
14
|
inputSchema,
|
|
15
15
|
outputSchema,
|
|
16
|
-
async handler(observation, { signal }) {
|
|
16
|
+
async handler(observation, { signal }: ToolContext) {
|
|
17
17
|
const collection = context.observations();
|
|
18
18
|
if (!collection) throw new Error("Observation provider unavailable");
|
|
19
19
|
signal.throwIfAborted();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MongoClient, type Collection, type Db } from "mongodb";
|
|
2
|
+
import type { AccessPolicy } from "@emseepea/server";
|
|
2
3
|
import type { PeaObservationDocument } from "./pea-observation-document.js";
|
|
3
4
|
import type { PeaDocument } from "./pea-document.js";
|
|
4
5
|
|
|
@@ -23,6 +24,7 @@ export function createDatabase(uri: string) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export interface MongoContext {
|
|
27
|
+
readonly access: AccessPolicy;
|
|
26
28
|
readonly varieties: () => Collection<PeaDocument> | undefined;
|
|
27
29
|
readonly observations: () => Collection<PeaObservationDocument> | undefined;
|
|
28
30
|
readonly database: () => Db | undefined;
|
|
@@ -2,7 +2,11 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import test, { after } from "node:test";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
insecureTestAuthentication,
|
|
7
|
+
startEmseepea,
|
|
8
|
+
startMcpServer,
|
|
9
|
+
} from "@emseepea/testing";
|
|
6
10
|
import { MongoClient } from "mongodb";
|
|
7
11
|
import { createMongoExample } from "../dist/app.js";
|
|
8
12
|
import {
|
|
@@ -57,6 +61,22 @@ test("each collection has one application schema and only varieties enforce it i
|
|
|
57
61
|
]);
|
|
58
62
|
});
|
|
59
63
|
|
|
64
|
+
test("the same template composes protected access and observability", async (t) => {
|
|
65
|
+
const events = [];
|
|
66
|
+
const permissions = ["catalogue:write"];
|
|
67
|
+
const { app } = await createMongoExample({
|
|
68
|
+
uri,
|
|
69
|
+
access: { access: "protected", requiredScopes: permissions },
|
|
70
|
+
authentication: insecureTestAuthentication(permissions),
|
|
71
|
+
observability: [{ id: "test-log", emit: (event) => events.push(event) }],
|
|
72
|
+
});
|
|
73
|
+
const running = await startEmseepea(t, app);
|
|
74
|
+
const client = await running.connect("test-token");
|
|
75
|
+
const result = await client.callTool({ name: "list-pea-varieties", arguments: {} });
|
|
76
|
+
assert.equal(result.isError, false);
|
|
77
|
+
assert.ok(events.some(({ capability }) => capability === "list-pea-varieties"));
|
|
78
|
+
});
|
|
79
|
+
|
|
60
80
|
test("setup fails closed when the schemaless collection already has a validator", async () => {
|
|
61
81
|
await database.command({
|
|
62
82
|
collMod: observationCollectionName,
|
package/package.json
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emseepea/create-mongodb-backed-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Create an Em See Pea server with schema-enforced and schemaless MongoDB collections.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"starterDependencies": [
|
|
8
|
-
"@emseepea/server",
|
|
9
|
-
"ajv",
|
|
10
|
-
"json-schema-to-ts",
|
|
11
|
-
"mongodb",
|
|
12
|
-
"zod"
|
|
13
|
-
],
|
|
7
|
+
"starterDependencies": ["@emseepea/server", "ajv", "json-schema-to-ts", "mongodb", "zod"],
|
|
14
8
|
"scripts": {
|
|
15
9
|
"build": "npm run build:example && npm run build:initializer",
|
|
16
10
|
"build:example": "tsc -p tsconfig.json",
|
|
@@ -26,34 +20,21 @@
|
|
|
26
20
|
"prepack": "npm run build:initializer"
|
|
27
21
|
},
|
|
28
22
|
"devDependencies": {
|
|
29
|
-
"@emseepea/server": "0.
|
|
23
|
+
"@emseepea/server": "0.4.0",
|
|
30
24
|
"ajv": "8.20.0",
|
|
31
25
|
"json-schema-to-ts": "3.1.1",
|
|
32
26
|
"mongodb": "7.6.0",
|
|
33
27
|
"zod": "4.4.3",
|
|
34
|
-
"@emseepea/testing": "0.
|
|
28
|
+
"@emseepea/testing": "0.6.1",
|
|
35
29
|
"@types/node": "24.13.3",
|
|
36
30
|
"typescript": "6.0.3",
|
|
37
31
|
"oxlint": "1.80.0"
|
|
38
32
|
},
|
|
39
|
-
"engines": {
|
|
40
|
-
|
|
41
|
-
},
|
|
42
|
-
"repository": {
|
|
43
|
-
"type": "git",
|
|
44
|
-
"url": "git+https://github.com/emseepea/emseepea.git",
|
|
45
|
-
"directory": "examples/mongodb-backed-server"
|
|
46
|
-
},
|
|
33
|
+
"engines": { "node": ">=22.13.0" },
|
|
34
|
+
"repository": { "type": "git", "url": "git+https://github.com/emseepea/emseepea.git", "directory": "examples/mongodb-backed-server" },
|
|
47
35
|
"homepage": "https://emseepea.github.io/emseepea/examples/",
|
|
48
36
|
"bugs": "https://github.com/emseepea/emseepea/issues",
|
|
49
|
-
"publishConfig": {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
},
|
|
53
|
-
"bin": {
|
|
54
|
-
"create-mongodb-backed-server": "./initializer-dist/create.mjs"
|
|
55
|
-
},
|
|
56
|
-
"files": [
|
|
57
|
-
"initializer-dist"
|
|
58
|
-
]
|
|
37
|
+
"publishConfig": { "access": "public", "provenance": true },
|
|
38
|
+
"bin": { "create-mongodb-backed-server": "./initializer-dist/create.mjs" },
|
|
39
|
+
"files": ["initializer-dist"]
|
|
59
40
|
}
|