@emseepea/create-soap-backed-server 0.0.1 → 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
@@ -26,6 +26,30 @@ lint checks, ordinary tests, and a semantic test.
26
26
 
27
27
  <!-- generated-project-readme -->
28
28
 
29
+ ## Choose Open or Protected Access
30
+
31
+ Start open when the catalogue and operations are public.
32
+
33
+ To protect this template, pass both options to the app factory:
34
+
35
+ - `access: { access: "protected", requiredScopes: ["peas:read"] }`
36
+ - an `authentication` adapter
37
+
38
+ Keep `authentication.discovery` as `"public"` unless capability names or
39
+ schemas are sensitive. Use `"protected"` only when each principal should see a
40
+ permission-filtered catalogue. OAuth metadata remains public in both modes.
41
+
42
+ ## Add Observability
43
+
44
+ The same factory accepts `observability`.
45
+
46
+ - Use `structuredLogging` for safe structured events.
47
+ - Use `openTelemetry` for traces and metrics.
48
+
49
+ Adapters receive only redacted framework events. They never receive request
50
+ bodies, arguments, results, tokens, provider claims, or raw errors. See the
51
+ [server API](https://github.com/emseepea/emseepea/tree/main/packages/framework#authentication-and-observability) for the complete configuration.
52
+
29
53
  ## SOAP-Backed Server
30
54
 
31
55
  The committed WSDL defines the operation and imports the committed service XSD.
@@ -1,4 +1,28 @@
1
- # SOAP-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
+ ## SOAP-Backed Server
2
26
 
3
27
  The committed WSDL defines the operation and imports the committed service XSD.
4
28
  That XSD generates `src/generated/pea-service.ts`. The same XSD is imported by
@@ -16,7 +16,7 @@
16
16
  "lint": "oxlint src test test-types eval scripts test-support"
17
17
  },
18
18
  "devDependencies": {
19
- "@emseepea/testing": "0.5.3",
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.3.3",
29
+ "@emseepea/server": "0.4.0",
30
30
  "@types/sax": "1.2.7",
31
31
  "soap": "1.11.0",
32
32
  "xml-xsd-engine": "1.7.3",
@@ -1,11 +1,21 @@
1
1
  import { fileURLToPath } from "node:url";
2
- import { createEmseepea, discoverCapabilities } from "@emseepea/server";
2
+ import {
3
+ createEmseepea,
4
+ discoverCapabilities,
5
+ type AccessPolicy,
6
+ type EmseepeaExtensions,
7
+ } from "@emseepea/server";
3
8
  import { createClientAsync } from "soap";
4
9
  import { z } from "zod";
5
10
  import { loadSoapEnvelopeSchema } from "./soap-schema.js";
6
11
  import { ValidatingHttpClient } from "./validating-http-client.js";
7
12
 
8
- export async function createSoapExample(endpointValue: string) {
13
+ export interface SoapExampleOptions extends EmseepeaExtensions {
14
+ readonly access?: AccessPolicy;
15
+ }
16
+
17
+ export async function createSoapExample(endpointValue: string, options: SoapExampleOptions = {}) {
18
+ const { access = { access: "public" }, ...extensions } = options;
9
19
  const endpoint = new URL(z.string().url().parse(endpointValue));
10
20
  if (!["http:", "https:"].includes(endpoint.protocol) || endpoint.username || endpoint.password || endpoint.hash) {
11
21
  throw new Error("SOAP endpoint must be an HTTP address without credentials or a fragment");
@@ -20,7 +30,8 @@ export async function createSoapExample(endpointValue: string) {
20
30
  name: "emseepea-soap-backed-server",
21
31
  version: "0.0.0",
22
32
  instructions: "Retrieve pea variety details from a legacy SOAP service.",
23
- ...await discoverCapabilities(new URL("./capabilities/", import.meta.url), { client }),
33
+ ...await discoverCapabilities(new URL("./capabilities/", import.meta.url), { client, access }),
34
+ ...extensions,
24
35
  });
25
36
  return { app };
26
37
  }
@@ -1,5 +1,7 @@
1
1
  import type { Client } from "soap";
2
+ import type { AccessPolicy } from "@emseepea/server";
2
3
 
3
4
  export interface SoapExampleContext {
4
5
  readonly client: Client;
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 { z } from "zod";
3
3
  import type { GetPeaRequest, GetPeaResponse } from "../generated/pea-service.js";
4
4
  import type { SoapExampleContext } from "./context.js";
@@ -14,13 +14,13 @@ const outputSchema = z.object({
14
14
  traits: z.array(z.string().max(100)).max(5).describe("Growing or eating traits."),
15
15
  });
16
16
 
17
- export default (({ client }) => defineTool({
17
+ export default (({ client, access }) => defineTool({
18
18
  name: "get-pea-variety",
19
- access: "public",
19
+ ...access,
20
20
  description: "Get details about one pea variety.",
21
21
  inputSchema,
22
22
  outputSchema,
23
- async handler({ name }, { signal }) {
23
+ async handler({ name }, { signal }: ToolContext) {
24
24
  signal.throwIfAborted();
25
25
  const request: GetPeaRequest = { name };
26
26
  const [response] = await client.GetPeaAsync(request, { signal });
@@ -2,7 +2,11 @@ import assert from "node:assert/strict";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import test 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 { createSoapExample } from "../dist/app.js";
7
11
  import { artifactsFromSources } from "../scripts/generate-types.mjs";
8
12
  import { parseSoapEnvelopeSchema } from "../dist/soap-schema.js";
@@ -90,6 +94,22 @@ test("retrieves XSD-validated SOAP data through a described MCP schema", async (
90
94
  });
91
95
  });
92
96
 
97
+ test("the same template composes protected access and observability", async (t) => {
98
+ const fixture = await startSoapFixture(t);
99
+ const events = [];
100
+ const permissions = ["varieties:read"];
101
+ const { app } = await createSoapExample(fixture.url.href, {
102
+ access: { access: "protected", requiredScopes: permissions },
103
+ authentication: insecureTestAuthentication(permissions),
104
+ observability: [{ id: "test-log", emit: (event) => events.push(event) }],
105
+ });
106
+ const running = await startEmseepea(t, app);
107
+ const client = await running.connect("test-token");
108
+ const result = await client.callTool({ name: "get-pea-variety", arguments: { name: "Sugar Ann" } });
109
+ assert.equal(result.isError, false);
110
+ assert.ok(events.some(({ capability }) => capability === "get-pea-variety"));
111
+ });
112
+
93
113
  test("rejects unsafe and invalid SOAP responses with generic public failures", async (t) => {
94
114
  const fixture = await startSoapFixture(t);
95
115
  const running = await startMcpServer(t, new URL("../dist/server.js", import.meta.url), {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emseepea/create-soap-backed-server",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Create an Em See Pea server that validates a SOAP service from its XSD contract.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,11 +20,11 @@
20
20
  "prepack": "npm run build:initializer"
21
21
  },
22
22
  "devDependencies": {
23
- "@emseepea/server": "0.3.3",
23
+ "@emseepea/server": "0.4.0",
24
24
  "soap": "1.11.0",
25
25
  "xml-xsd-engine": "1.7.3",
26
26
  "zod": "4.4.3",
27
- "@emseepea/testing": "0.5.3",
27
+ "@emseepea/testing": "0.6.1",
28
28
  "@types/node": "24.13.3",
29
29
  "@types/sax": "1.2.7",
30
30
  "typescript": "6.0.3",