@emseepea/create-resources-and-prompts-server 0.0.13 → 0.0.17
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 +31 -0
- package/initializer-dist/template/README.md +32 -1
- package/initializer-dist/template/eval/meaning.test.mjs +12 -4
- package/initializer-dist/template/package.json +3 -2
- package/initializer-dist/template/src/app.ts +20 -0
- package/initializer-dist/template/src/capabilities/prompt.growing-guide.ts +4 -3
- package/initializer-dist/template/src/capabilities/resource.getting-started.ts +4 -3
- package/initializer-dist/template/src/capabilities/resource.planting-guide.ts +4 -3
- package/initializer-dist/template/src/server.ts +5 -10
- package/initializer-dist/template/test/server.test.mjs +20 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -17,6 +17,30 @@ npm init @emseepea/resources-and-prompts-server -- my-server
|
|
|
17
17
|
|
|
18
18
|
<!-- generated-project-readme -->
|
|
19
19
|
|
|
20
|
+
## Choose Open or Protected Access
|
|
21
|
+
|
|
22
|
+
Start open when the catalogue and operations are public.
|
|
23
|
+
|
|
24
|
+
To protect this template, pass both options to the app factory:
|
|
25
|
+
|
|
26
|
+
- `access: { access: "protected", requiredScopes: ["peas:read"] }`
|
|
27
|
+
- an `authentication` adapter
|
|
28
|
+
|
|
29
|
+
Keep `authentication.discovery` as `"public"` unless capability names or
|
|
30
|
+
schemas are sensitive. Use `"protected"` only when each principal should see a
|
|
31
|
+
permission-filtered catalogue. OAuth metadata remains public in both modes.
|
|
32
|
+
|
|
33
|
+
## Add Observability
|
|
34
|
+
|
|
35
|
+
The same factory accepts `observability`.
|
|
36
|
+
|
|
37
|
+
- Use `structuredLogging` for safe structured events.
|
|
38
|
+
- Use `openTelemetry` for traces and metrics.
|
|
39
|
+
|
|
40
|
+
Adapters receive only redacted framework events. They never receive request
|
|
41
|
+
bodies, arguments, results, tokens, provider claims, or raw errors. See the
|
|
42
|
+
[server API](https://github.com/emseepea/emseepea/tree/main/packages/framework#authentication-and-observability) for the complete configuration.
|
|
43
|
+
|
|
20
44
|
## Resources and Prompts Example
|
|
21
45
|
|
|
22
46
|
Choose this example when you want to give an assistant reusable reference
|
|
@@ -42,6 +66,13 @@ npm start
|
|
|
42
66
|
The server listens on `http://127.0.0.1:3000/mcp` by default. Set `PORT` to
|
|
43
67
|
choose another port.
|
|
44
68
|
|
|
69
|
+
## Add Feedback
|
|
70
|
+
|
|
71
|
+
Install `@emseepea/feedback` when this server needs a detailed one-way
|
|
72
|
+
observation or a durable support conversation. Pass its tools through the
|
|
73
|
+
application factory's `additionalTools` option. Choose PostgreSQL, Firestore,
|
|
74
|
+
GitHub Issues, or Zendesk in the [feedback guide](../../packages/feedback/README.md).
|
|
75
|
+
|
|
45
76
|
## Check This Example
|
|
46
77
|
|
|
47
78
|
[Ordinary tests](test/) live in `test/`.
|
|
@@ -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
|
+
## Resources and Prompts Example
|
|
2
26
|
|
|
3
27
|
Choose this example when you want to give an assistant reusable reference
|
|
4
28
|
content and guided starting questions, without adding another tool.
|
|
@@ -23,6 +47,13 @@ npm start
|
|
|
23
47
|
The server listens on `http://127.0.0.1:3000/mcp` by default. Set `PORT` to
|
|
24
48
|
choose another port.
|
|
25
49
|
|
|
50
|
+
## Add Feedback
|
|
51
|
+
|
|
52
|
+
Install `@emseepea/feedback` when this server needs a detailed one-way
|
|
53
|
+
observation or a durable support conversation. Pass its tools through the
|
|
54
|
+
application factory's `additionalTools` option. Choose PostgreSQL, Firestore,
|
|
55
|
+
GitHub Issues, or Zendesk in the [feedback guide](../../packages/feedback/README.md).
|
|
56
|
+
|
|
26
57
|
## Check This Example
|
|
27
58
|
|
|
28
59
|
[Ordinary tests](test/) live in `test/`.
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
assertOptionalToolCall,
|
|
4
4
|
assertResponseMeaning,
|
|
5
5
|
createConversation,
|
|
6
6
|
} from "@emseepea/testing/semantic";
|
|
7
7
|
|
|
8
|
+
const server = new URL(import.meta.resolve("@emseepea/feedback/testing-server"));
|
|
9
|
+
const environment = {
|
|
10
|
+
EMSEEPEA_EVAL_APP_MODULE: new URL("../dist/app.js", import.meta.url).href,
|
|
11
|
+
EMSEEPEA_EVAL_APP_FACTORY: "createResourcesAndPromptsServer",
|
|
12
|
+
};
|
|
13
|
+
|
|
8
14
|
test("does not pretend an unselected resource was supplied", async (t) => {
|
|
9
15
|
const chat = await createConversation(t, {
|
|
10
|
-
server
|
|
16
|
+
server,
|
|
17
|
+
environment,
|
|
11
18
|
});
|
|
12
19
|
|
|
13
20
|
const response = await chat.send(
|
|
@@ -15,9 +22,10 @@ test("does not pretend an unselected resource was supplied", async (t) => {
|
|
|
15
22
|
"and plant spacing does guide://peas/planting/container specify?",
|
|
16
23
|
);
|
|
17
24
|
|
|
18
|
-
assertNoToolCalls(response);
|
|
19
25
|
// Resources and prompts are user-selected client features, not autonomous
|
|
20
|
-
// tools.
|
|
26
|
+
// tools. Capability-mismatch feedback is valid in this deliberately
|
|
27
|
+
// unsuccessful journey, so this test checks only the honest response.
|
|
28
|
+
assertOptionalToolCall(response, "submit-feedback");
|
|
21
29
|
await assertResponseMeaning(response, {
|
|
22
30
|
expected:
|
|
23
31
|
"The response does not claim to know the guide's recommendation. It says " +
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"lint": "oxlint src test eval"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@emseepea/
|
|
17
|
+
"@emseepea/feedback": "0.2.0",
|
|
18
|
+
"@emseepea/testing": "0.9.3",
|
|
18
19
|
"@types/node": "24.13.3",
|
|
19
20
|
"typescript": "6.0.3",
|
|
20
21
|
"oxlint": "1.80.0"
|
|
@@ -24,7 +25,7 @@
|
|
|
24
25
|
},
|
|
25
26
|
"private": true,
|
|
26
27
|
"dependencies": {
|
|
27
|
-
"@emseepea/server": "0.
|
|
28
|
+
"@emseepea/server": "0.6.1",
|
|
28
29
|
"zod": "4.4.3"
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createEmseepea,
|
|
3
|
+
discoverCapabilities,
|
|
4
|
+
type AccessPolicy,
|
|
5
|
+
type EmseepeaExtensions,
|
|
6
|
+
} from "@emseepea/server";
|
|
7
|
+
|
|
8
|
+
export interface ResourcesAndPromptsServerOptions extends EmseepeaExtensions {
|
|
9
|
+
readonly access?: AccessPolicy;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function createResourcesAndPromptsServer(options: ResourcesAndPromptsServerOptions = {}) {
|
|
13
|
+
const { access = { access: "public" }, ...extensions } = options;
|
|
14
|
+
return createEmseepea({
|
|
15
|
+
name: "emseepea-resources-and-prompts-server",
|
|
16
|
+
version: "0.0.0",
|
|
17
|
+
...await discoverCapabilities(new URL("./capabilities/", import.meta.url), access),
|
|
18
|
+
...extensions,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { definePrompt, type CapabilityModuleFactory } from "@emseepea/server";
|
|
1
|
+
import { definePrompt, type AccessPolicy, type CapabilityModuleFactory } from "@emseepea/server";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
const topics = ["sowing-depth", "plant-spacing", "trellis-support"];
|
|
5
5
|
|
|
6
|
-
export default (() => definePrompt({
|
|
6
|
+
export default ((access) => definePrompt({
|
|
7
|
+
...access,
|
|
7
8
|
name: "growing-guide",
|
|
8
9
|
title: "Pea growing guide",
|
|
9
10
|
description: "Create a prompt for a sample pea-growing topic.",
|
|
@@ -23,4 +24,4 @@ export default (() => definePrompt({
|
|
|
23
24
|
},
|
|
24
25
|
}],
|
|
25
26
|
}),
|
|
26
|
-
})) satisfies CapabilityModuleFactory
|
|
27
|
+
})) satisfies CapabilityModuleFactory<AccessPolicy>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { defineResource, type CapabilityModuleFactory } from "@emseepea/server";
|
|
1
|
+
import { defineResource, type AccessPolicy, type CapabilityModuleFactory } from "@emseepea/server";
|
|
2
2
|
|
|
3
3
|
const guideUri = "guide://peas/getting-started";
|
|
4
4
|
|
|
5
|
-
export default (() => defineResource({
|
|
5
|
+
export default ((access) => defineResource({
|
|
6
|
+
...access,
|
|
6
7
|
name: "getting-started",
|
|
7
8
|
uri: guideUri,
|
|
8
9
|
title: "Pea growing: getting started",
|
|
@@ -15,4 +16,4 @@ export default (() => defineResource({
|
|
|
15
16
|
text: "# Plant peas clearly\n\nSowing depth is how deep a seed goes; plant spacing is the gap between plants. They are separate choices, not interchangeable measurements.\n",
|
|
16
17
|
}],
|
|
17
18
|
}),
|
|
18
|
-
})) satisfies CapabilityModuleFactory
|
|
19
|
+
})) satisfies CapabilityModuleFactory<AccessPolicy>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { defineResourceTemplate, type CapabilityModuleFactory } from "@emseepea/server";
|
|
1
|
+
import { defineResourceTemplate, type AccessPolicy, type CapabilityModuleFactory } from "@emseepea/server";
|
|
2
2
|
|
|
3
3
|
const methods = ["container", "raised-bed", "row"];
|
|
4
4
|
|
|
5
|
-
export default (() => defineResourceTemplate({
|
|
5
|
+
export default ((access) => defineResourceTemplate({
|
|
6
|
+
...access,
|
|
6
7
|
name: "planting-guide",
|
|
7
8
|
uriTemplate: "guide://peas/planting/{method}",
|
|
8
9
|
title: "Pea planting guide",
|
|
@@ -18,4 +19,4 @@ export default (() => defineResourceTemplate({
|
|
|
18
19
|
text: `# ${String(variables.method)}\n`,
|
|
19
20
|
}],
|
|
20
21
|
}),
|
|
21
|
-
})) satisfies CapabilityModuleFactory
|
|
22
|
+
})) satisfies CapabilityModuleFactory<AccessPolicy>;
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
discoverCapabilities,
|
|
4
|
-
serveEmseepea,
|
|
5
|
-
} from "@emseepea/server";
|
|
1
|
+
import { serveEmseepea } from "@emseepea/server";
|
|
2
|
+
import { createResourcesAndPromptsServer } from "./app.js";
|
|
6
3
|
|
|
7
|
-
const running = await serveEmseepea(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
...await discoverCapabilities(new URL("./capabilities/", import.meta.url)),
|
|
11
|
-
}), { port: Number.parseInt(process.env.PORT ?? "3000", 10) });
|
|
4
|
+
const running = await serveEmseepea(await createResourcesAndPromptsServer(), {
|
|
5
|
+
port: Number.parseInt(process.env.PORT ?? "3000", 10),
|
|
6
|
+
});
|
|
12
7
|
|
|
13
8
|
console.log(`Em See Pea resources and prompts example listening at ${running.url}`);
|
|
14
9
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
insecureTestAuthentication,
|
|
6
|
+
startEmseepea,
|
|
7
|
+
startMcpServer,
|
|
8
|
+
} from "@emseepea/testing";
|
|
9
|
+
import { createResourcesAndPromptsServer } from "../dist/app.js";
|
|
5
10
|
|
|
6
11
|
test("lists and reads the advertised resource and prompt", async (t) => {
|
|
7
12
|
const running = await startMcpServer(t, new URL("../dist/server.js", import.meta.url));
|
|
@@ -27,3 +32,17 @@ test("lists and reads the advertised resource and prompt", async (t) => {
|
|
|
27
32
|
});
|
|
28
33
|
assert.match(prompt.messages[0].content.text, /Explain sowing-depth/);
|
|
29
34
|
});
|
|
35
|
+
|
|
36
|
+
test("the same template composes protected access and observability", async (t) => {
|
|
37
|
+
const events = [];
|
|
38
|
+
const permissions = ["guides:read"];
|
|
39
|
+
const running = await startEmseepea(t, await createResourcesAndPromptsServer({
|
|
40
|
+
access: { access: "protected", requiredScopes: permissions },
|
|
41
|
+
authentication: insecureTestAuthentication(permissions),
|
|
42
|
+
observability: [{ id: "test-log", emit: (event) => events.push(event) }],
|
|
43
|
+
}));
|
|
44
|
+
const client = await running.connect("test-token");
|
|
45
|
+
assert.deepEqual((await client.listResources()).resources.map(({ name }) => name), ["getting-started"]);
|
|
46
|
+
assert.deepEqual((await client.listPrompts()).prompts.map(({ name }) => name), ["growing-guide"]);
|
|
47
|
+
assert.ok(events.some(({ method }) => method === "resources/list"));
|
|
48
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emseepea/create-resources-and-prompts-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "Create an Em See Pea server with resources and prompts.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
"prepack": "npm run build:initializer"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@emseepea/
|
|
21
|
+
"@emseepea/feedback": "0.2.0",
|
|
22
|
+
"@emseepea/server": "0.6.1",
|
|
22
23
|
"zod": "4.4.3",
|
|
23
|
-
"@emseepea/testing": "0.
|
|
24
|
+
"@emseepea/testing": "0.9.3",
|
|
24
25
|
"@types/node": "24.13.3",
|
|
25
26
|
"typescript": "6.0.3",
|
|
26
27
|
"oxlint": "1.80.0"
|