@emseepea/create-mongodb-backed-server 0.0.1 → 0.0.5

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
@@ -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
@@ -90,6 +114,13 @@ The tools never accept database operators, collection names, sort documents,
90
114
  or destinations. The pool and database operations are bounded, and provider
91
115
  failures return a generic error.
92
116
 
117
+ ## Add Feedback
118
+
119
+ Install `@emseepea/feedback` when this server needs a detailed one-way
120
+ observation or a durable support conversation. Pass its tools through the
121
+ application factory's `additionalTools` option. Choose PostgreSQL, Firestore,
122
+ GitHub Issues, or Zendesk in the [feedback guide](../../packages/feedback/README.md).
123
+
93
124
  ## Check This Project
94
125
 
95
126
  Run lint, build, and ordinary database integration tests without spending model
@@ -1,4 +1,28 @@
1
- # MongoDB-Backed 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
+ ## 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
@@ -61,6 +85,13 @@ The tools never accept database operators, collection names, sort documents,
61
85
  or destinations. The pool and database operations are bounded, and provider
62
86
  failures return a generic error.
63
87
 
88
+ ## Add Feedback
89
+
90
+ Install `@emseepea/feedback` when this server needs a detailed one-way
91
+ observation or a durable support conversation. Pass its tools through the
92
+ application factory's `additionalTools` option. Choose PostgreSQL, Firestore,
93
+ GitHub Issues, or Zendesk in the [feedback guide](../../packages/feedback/README.md).
94
+
64
95
  ## Check This Project
65
96
 
66
97
  Run lint, build, and ordinary database integration tests without spending model
@@ -1,11 +1,15 @@
1
1
  import test from "node:test";
2
2
  import {
3
+ assertNoNegativeFeedback,
3
4
  assertResponseContains,
4
5
  assertResponseMeaning,
5
- assertToolCalls,
6
+ assertToolCallsWithOptionalFeedback,
6
7
  createConversation,
7
8
  } from "@emseepea/testing/semantic";
8
9
 
10
+ const server = new URL(import.meta.resolve("@emseepea/feedback/testing-server"));
11
+ const appModule = new URL("../dist/app.js", import.meta.url).href;
12
+
9
13
  const trialUris = [1, 2, 3].map((trial) => {
10
14
  const value = process.env[`MONGODB_URL_TRIAL_${trial}`];
11
15
  if (!value) throw new Error(`MONGODB_URL_TRIAL_${trial} is required`);
@@ -14,14 +18,19 @@ const trialUris = [1, 2, 3].map((trial) => {
14
18
 
15
19
  test("finds and adds MongoDB-backed pea varieties through natural requests", async (t) => {
16
20
  const chat = await createConversation(t, {
17
- server: new URL("../dist/server.js", import.meta.url),
18
- environment: (trial) => ({ MONGODB_URL: trialUris[trial - 1] }),
21
+ server,
22
+ environment: (trial) => ({
23
+ MONGODB_URL: trialUris[trial - 1],
24
+ EMSEEPEA_EVAL_APP_MODULE: appModule,
25
+ EMSEEPEA_EVAL_APP_FACTORY: "createMongoExample",
26
+ EMSEEPEA_EVAL_APP_KIND: "mongodb",
27
+ }),
19
28
  });
20
29
 
21
30
  // The read and write turns cover both public decisions. Storage validation
22
31
  // remains in deterministic tests because asking a model cannot prove it.
23
32
  const fastest = await chat.send("Among the saved snap pea varieties, which matures fastest?");
24
- assertToolCalls(fastest, [{
33
+ await assertToolCallsWithOptionalFeedback(fastest, [{
25
34
  name: "list-pea-varieties",
26
35
  arguments: { pea_type: "snap" },
27
36
  }]);
@@ -33,7 +42,7 @@ test("finds and adds MongoDB-backed pea varieties through natural requests", asy
33
42
  "Add Golden Sweet as a climbing mangetout pea that matures in 70 days. " +
34
43
  "Use exactly mangetout as its pea type. Its notes are: Purple flowers and flat edible pods.",
35
44
  );
36
- assertToolCalls(added, [{
45
+ await assertToolCallsWithOptionalFeedback(added, [{
37
46
  name: "add-pea-variety",
38
47
  arguments: {
39
48
  name: "Golden Sweet",
@@ -51,7 +60,7 @@ test("finds and adds MongoDB-backed pea varieties through natural requests", asy
51
60
  "Record that Golden Sweet was flowering in the west trellis on 2026-09-08. " +
52
61
  "The notes are: First flower opened.",
53
62
  );
54
- assertToolCalls(recorded, [{
63
+ await assertToolCallsWithOptionalFeedback(recorded, [{
55
64
  name: "record-pea-observation",
56
65
  arguments: {
57
66
  variety_name: "Golden Sweet",
@@ -63,7 +72,7 @@ test("finds and adds MongoDB-backed pea varieties through natural requests", asy
63
72
  }]);
64
73
 
65
74
  const observations = await chat.send("What have I observed about Golden Sweet?");
66
- assertToolCalls(observations, [{
75
+ await assertToolCallsWithOptionalFeedback(observations, [{
67
76
  name: "list-pea-observations",
68
77
  arguments: { variety_name: "Golden Sweet" },
69
78
  }]);
@@ -72,4 +81,5 @@ test("finds and adds MongoDB-backed pea varieties through natural requests", asy
72
81
  "Golden Sweet was flowering in the west trellis on 8 September 2026, " +
73
82
  "and the first flower had opened.",
74
83
  });
84
+ assertNoNegativeFeedback(fastest, added, recorded, observations);
75
85
  });
@@ -16,7 +16,8 @@
16
16
  "lint": "oxlint src test eval"
17
17
  },
18
18
  "devDependencies": {
19
- "@emseepea/testing": "0.5.3",
19
+ "@emseepea/feedback": "0.2.0",
20
+ "@emseepea/testing": "0.9.3",
20
21
  "@types/node": "24.13.3",
21
22
  "typescript": "6.0.3",
22
23
  "oxlint": "1.80.0"
@@ -26,7 +27,7 @@
26
27
  },
27
28
  "private": true,
28
29
  "dependencies": {
29
- "@emseepea/server": "0.3.3",
30
+ "@emseepea/server": "0.6.1",
30
31
  "ajv": "8.20.0",
31
32
  "json-schema-to-ts": "3.1.1",
32
33
  "mongodb": "7.6.0",
@@ -1,13 +1,25 @@
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 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 { readonly uri: string }
13
+ export interface MongoExampleOptions extends EmseepeaExtensions {
14
+ readonly uri: string;
15
+ readonly access?: AccessPolicy;
16
+ }
9
17
 
10
- export async function createMongoExample({ uri }: MongoExampleOptions) {
18
+ export async function createMongoExample({
19
+ uri,
20
+ access = { access: "public" },
21
+ ...extensions
22
+ }: MongoExampleOptions) {
11
23
  const parsedUri = z.string().url().refine(
12
24
  (value) => ["mongodb:", "mongodb+srv:"].includes(new URL(value).protocol),
13
25
  "uri must use MongoDB",
@@ -33,10 +45,12 @@ export async function createMongoExample({ uri }: MongoExampleOptions) {
33
45
  },
34
46
  readinessTimeoutMs: 2_500,
35
47
  ...await discoverCapabilities(new URL("./capabilities/", import.meta.url), {
48
+ access,
36
49
  database: () => database,
37
50
  observations: () => observations,
38
51
  varieties: () => varieties,
39
52
  }),
53
+ ...extensions,
40
54
  });
41
55
  const closeProvider = async () => {
42
56
  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: "public",
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: "public",
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: "public",
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: "public",
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 { startMcpServer } from "@emseepea/testing";
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@emseepea/create-mongodb-backed-server",
3
- "version": "0.0.1",
3
+ "version": "0.0.5",
4
4
  "description": "Create an Em See Pea server with schema-enforced and schemaless MongoDB collections.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,12 +20,13 @@
20
20
  "prepack": "npm run build:initializer"
21
21
  },
22
22
  "devDependencies": {
23
- "@emseepea/server": "0.3.3",
23
+ "@emseepea/feedback": "0.2.0",
24
+ "@emseepea/server": "0.6.1",
24
25
  "ajv": "8.20.0",
25
26
  "json-schema-to-ts": "3.1.1",
26
27
  "mongodb": "7.6.0",
27
28
  "zod": "4.4.3",
28
- "@emseepea/testing": "0.5.3",
29
+ "@emseepea/testing": "0.9.3",
29
30
  "@types/node": "24.13.3",
30
31
  "typescript": "6.0.3",
31
32
  "oxlint": "1.80.0"