opedd-mcp 0.3.0 → 0.4.1

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
@@ -45,7 +45,7 @@ Exposes up to 16 tools to any AI assistant (some are conditional on env vars):
45
45
  | `get_compliance_dossier` | Procurement-defense compliance dossier mapping retrievals to license terms (Phase 11 M4) |
46
46
  | `article_53_attestation` | Signed JWT attesting EU AI Act Article 53(1)(d) compliance for a license — the artifact AI labs hand to legal/procurement (Phase 12 W1.4) |
47
47
 
48
- **Requires `OPEDD_API_KEY` (op_*) — publisher-side**
48
+ **Requires `OPEDD_PUB_BEARER` (opedd_pub_*) — publisher-side**
49
49
 
50
50
  | Tool | Description |
51
51
  |------|-------------|
@@ -84,7 +84,8 @@ Set environment variables to pre-configure the server:
84
84
  | `OPEDD_BUYER_TOKEN` | Optional | Buyer API token (`opedd_buyer_live_*` canonical; `opedd_buyer_test_*` for sandbox) — enables `get_content` |
85
85
  | `OPEDD_ACCESS_KEY` | Optional | Enterprise access key (`ent_*`) — enables `list_feed` + `stream_feed_ndjson` |
86
86
  | `OPEDD_BUYER_JWT` | Optional | Supabase session JWT from the buyer portal — enables `get_audit_events` + `get_compliance_dossier` |
87
- | `OPEDD_API_KEY` | Optional | Publisher API key (`op_...`) — enables `list_publisher_content` |
87
+ | `OPEDD_PUB_BEARER` | Optional | Canonical Publisher API Bearer key (`opedd_pub_<env>_<32-hex>`; issued via `POST /publishers-api-keys action=create_api_key`) — enables `list_publisher_content`. **v0.4.0 canonical.** |
88
+ | `OPEDD_API_KEY` | Deprecated | Legacy Publisher API key (`op_...`) — fallback during the transition window; will stop working when opedd-backend Phase C deploys. Migrate to `OPEDD_PUB_BEARER`. |
88
89
  | `OPEDD_API_URL` | Optional | Override the API base URL (default: Opedd production) |
89
90
 
90
91
  > **Getting a Stripe payment method ID**: Save a card in your Stripe account and retrieve the `pm_...` ID via the [Stripe API](https://stripe.com/docs/api/payment_methods).
package/dist/index.js CHANGED
@@ -8,7 +8,16 @@ const API_BASE = process.env.OPEDD_API_URL ??
8
8
  "https://api.opedd.com";
9
9
  const BUYER_EMAIL = process.env.OPEDD_BUYER_EMAIL;
10
10
  const PAYMENT_METHOD_ID = process.env.OPEDD_PAYMENT_METHOD_ID;
11
- const API_KEY = process.env.OPEDD_API_KEY; // publisher API key (op_...)
11
+ // B91 v0.4.0 migration (2026-05-26): canonical Bearer auth.
12
+ // OPEDD_PUB_BEARER is the new canonical opedd_pub_<env>_<32-hex> key
13
+ // (issued via POST /publishers-api-keys action=create_api_key).
14
+ // OPEDD_API_KEY retained as backward-compat alias for legacy op_ keys
15
+ // during the dual-mode transition window on the /api endpoint (Phase A
16
+ // shipped opedd-backend 2026-05-26). Backend Phase C will drop the
17
+ // op_ path entirely; users MUST migrate to OPEDD_PUB_BEARER before
18
+ // then. Prefer Bearer if both set.
19
+ const PUB_BEARER = process.env.OPEDD_PUB_BEARER; // canonical opedd_pub_<env>_<32-hex>
20
+ const API_KEY = process.env.OPEDD_API_KEY; // LEGACY op_<32-hex> — retiring per backend Phase C
12
21
  const BUYER_TOKEN = process.env.OPEDD_BUYER_TOKEN; // buyer API token (opedd_buyer_live_... or opedd_buyer_test_...)
13
22
  const ACCESS_KEY = process.env.OPEDD_ACCESS_KEY; // enterprise access key (ent_*); for /enterprise-license GET feed
14
23
  const BUYER_JWT = process.env.OPEDD_BUYER_JWT; // Supabase JWT; for /buyer-audit + /buyer-compliance-report
@@ -17,7 +26,9 @@ async function opeddFetch(path, options = {}) {
17
26
  const url = `${API_BASE}${path}`;
18
27
  const headers = {
19
28
  "Content-Type": "application/json",
20
- ...(API_KEY ? { "X-API-Key": API_KEY } : {}),
29
+ // B91 v0.4.0 (2026-05-26): canonical Bearer preferred; legacy X-API-Key
30
+ // accepted only as fallback during transition window
31
+ ...(PUB_BEARER ? { Authorization: `Bearer ${PUB_BEARER}` } : (API_KEY ? { "X-API-Key": API_KEY } : {})),
21
32
  ...(options.headers ?? {}),
22
33
  };
23
34
  const res = await fetch(url, { ...options, headers });
@@ -35,7 +46,8 @@ async function opeddFetchNdjson(path, options = {}) {
35
46
  const url = `${API_BASE}${path}`;
36
47
  const headers = {
37
48
  Accept: "application/x-ndjson",
38
- ...(API_KEY ? { "X-API-Key": API_KEY } : {}),
49
+ // B91 v0.4.0 (2026-05-26): canonical Bearer preferred; legacy fallback
50
+ ...(PUB_BEARER ? { Authorization: `Bearer ${PUB_BEARER}` } : (API_KEY ? { "X-API-Key": API_KEY } : {})),
39
51
  ...(options.headers ?? {}),
40
52
  };
41
53
  const res = await fetch(url, { ...options, headers });
@@ -527,10 +539,10 @@ if (BUYER_JWT) {
527
539
  });
528
540
  }
529
541
  // If a publisher API key is configured, expose publisher-specific tooling
530
- if (API_KEY) {
542
+ if (PUB_BEARER || API_KEY) {
531
543
  TOOLS.push({
532
544
  name: "list_publisher_content",
533
- description: "List all licensable articles for the authenticated publisher (requires OPEDD_API_KEY). " +
545
+ description: "List all licensable articles for the authenticated publisher (requires OPEDD_PUB_BEARER, or legacy OPEDD_API_KEY). " +
534
546
  "Returns articles with titles, descriptions, pricing, and sales statistics. " +
535
547
  "Use article IDs from this list to purchase licenses via purchase_license.",
536
548
  inputSchema: {
@@ -815,8 +827,8 @@ export async function dispatchTool(name, args) {
815
827
  }
816
828
  // ── list_publisher_content ─────────────────────────────────────────────
817
829
  case "list_publisher_content": {
818
- if (!API_KEY) {
819
- return err("OPEDD_API_KEY env var is required for this tool");
830
+ if (!PUB_BEARER && !API_KEY) {
831
+ return err("OPEDD_PUB_BEARER env var is required for this tool (canonical Bearer; legacy OPEDD_API_KEY also accepted during transition)");
820
832
  }
821
833
  const { limit = 20, type, offset = 0 } = args;
822
834
  const params = new URLSearchParams({ action: "articles" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opedd-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "MCP server for Opedd — discover, purchase, verify, and audit content licenses from AI assistants. Covers Phase 11 buyer-side surfaces + Phase 12 Wave 1 + 3 regulatory surfaces (CDSM Article 4(3) RSL + EU AI Act Article 53 attestation + platform onboarding helper).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,7 +12,8 @@
12
12
  "start": "node dist/index.js",
13
13
  "dev": "tsx src/index.ts",
14
14
  "test": "vitest run",
15
- "test:watch": "vitest"
15
+ "test:watch": "vitest",
16
+ "prepublishOnly": "npm run build && npm test"
16
17
  },
17
18
  "keywords": [
18
19
  "mcp",
package/src/index.ts CHANGED
@@ -17,7 +17,16 @@ const API_BASE =
17
17
 
18
18
  const BUYER_EMAIL = process.env.OPEDD_BUYER_EMAIL;
19
19
  const PAYMENT_METHOD_ID = process.env.OPEDD_PAYMENT_METHOD_ID;
20
- const API_KEY = process.env.OPEDD_API_KEY; // publisher API key (op_...)
20
+ // B91 v0.4.0 migration (2026-05-26): canonical Bearer auth.
21
+ // OPEDD_PUB_BEARER is the new canonical opedd_pub_<env>_<32-hex> key
22
+ // (issued via POST /publishers-api-keys action=create_api_key).
23
+ // OPEDD_API_KEY retained as backward-compat alias for legacy op_ keys
24
+ // during the dual-mode transition window on the /api endpoint (Phase A
25
+ // shipped opedd-backend 2026-05-26). Backend Phase C will drop the
26
+ // op_ path entirely; users MUST migrate to OPEDD_PUB_BEARER before
27
+ // then. Prefer Bearer if both set.
28
+ const PUB_BEARER = process.env.OPEDD_PUB_BEARER; // canonical opedd_pub_<env>_<32-hex>
29
+ const API_KEY = process.env.OPEDD_API_KEY; // LEGACY op_<32-hex> — retiring per backend Phase C
21
30
  const BUYER_TOKEN = process.env.OPEDD_BUYER_TOKEN; // buyer API token (opedd_buyer_live_... or opedd_buyer_test_...)
22
31
  const ACCESS_KEY = process.env.OPEDD_ACCESS_KEY; // enterprise access key (ent_*); for /enterprise-license GET feed
23
32
  const BUYER_JWT = process.env.OPEDD_BUYER_JWT; // Supabase JWT; for /buyer-audit + /buyer-compliance-report
@@ -28,7 +37,9 @@ async function opeddFetch(path: string, options: RequestInit = {}): Promise<unkn
28
37
  const url = `${API_BASE}${path}`;
29
38
  const headers: Record<string, string> = {
30
39
  "Content-Type": "application/json",
31
- ...(API_KEY ? { "X-API-Key": API_KEY } : {}),
40
+ // B91 v0.4.0 (2026-05-26): canonical Bearer preferred; legacy X-API-Key
41
+ // accepted only as fallback during transition window
42
+ ...(PUB_BEARER ? { Authorization: `Bearer ${PUB_BEARER}` } : (API_KEY ? { "X-API-Key": API_KEY } : {})),
32
43
  ...(options.headers as Record<string, string> ?? {}),
33
44
  };
34
45
  const res = await fetch(url, { ...options, headers });
@@ -50,7 +61,8 @@ async function opeddFetchNdjson(
50
61
  const url = `${API_BASE}${path}`;
51
62
  const headers: Record<string, string> = {
52
63
  Accept: "application/x-ndjson",
53
- ...(API_KEY ? { "X-API-Key": API_KEY } : {}),
64
+ // B91 v0.4.0 (2026-05-26): canonical Bearer preferred; legacy fallback
65
+ ...(PUB_BEARER ? { Authorization: `Bearer ${PUB_BEARER}` } : (API_KEY ? { "X-API-Key": API_KEY } : {})),
54
66
  ...(options.headers as Record<string, string> ?? {}),
55
67
  };
56
68
  const res = await fetch(url, { ...options, headers });
@@ -570,11 +582,11 @@ if (BUYER_JWT) {
570
582
  }
571
583
 
572
584
  // If a publisher API key is configured, expose publisher-specific tooling
573
- if (API_KEY) {
585
+ if (PUB_BEARER || API_KEY) {
574
586
  TOOLS.push({
575
587
  name: "list_publisher_content",
576
588
  description:
577
- "List all licensable articles for the authenticated publisher (requires OPEDD_API_KEY). " +
589
+ "List all licensable articles for the authenticated publisher (requires OPEDD_PUB_BEARER, or legacy OPEDD_API_KEY). " +
578
590
  "Returns articles with titles, descriptions, pricing, and sales statistics. " +
579
591
  "Use article IDs from this list to purchase licenses via purchase_license.",
580
592
  inputSchema: {
@@ -977,8 +989,8 @@ export async function dispatchTool(
977
989
 
978
990
  // ── list_publisher_content ─────────────────────────────────────────────
979
991
  case "list_publisher_content": {
980
- if (!API_KEY) {
981
- return err("OPEDD_API_KEY env var is required for this tool");
992
+ if (!PUB_BEARER && !API_KEY) {
993
+ return err("OPEDD_PUB_BEARER env var is required for this tool (canonical Bearer; legacy OPEDD_API_KEY also accepted during transition)");
982
994
  }
983
995
 
984
996
  const { limit = 20, type, offset = 0 } = args as {
@@ -4,7 +4,7 @@
4
4
  // article_53_attestation, get_buyer_account, get_audit_events,
5
5
  // get_compliance_dossier (require OPEDD_BUYER_JWT)
6
6
  // list_feed, stream_feed_ndjson (require OPEDD_ACCESS_KEY)
7
- // list_publisher_content (requires OPEDD_API_KEY)
7
+ // list_publisher_content (requires OPEDD_PUB_BEARER OR legacy OPEDD_API_KEY)
8
8
  //
9
9
  // Separate file from dispatcher.test.ts because the TOOLS array push +
10
10
  // CallTool switch cases evaluate env-var presence at module-import time;
@@ -18,6 +18,7 @@ const TEST_BUYER_TOKEN = "opedd_buyer_test_env_gated_xxx";
18
18
  const TEST_BUYER_JWT = "eyJhbGc.env-gated-test.sig";
19
19
  const TEST_ACCESS_KEY = "ent_env_gated_test";
20
20
  const TEST_API_KEY = "op_env_gated_test_pub";
21
+ const TEST_PUB_BEARER = "opedd_pub_test_env_gated_test_pub_canonical_xxxx"; // v0.4.0 canonical Bearer
21
22
  const TEST_BUYER_EMAIL = "env-gated@opedd-test.com";
22
23
 
23
24
  // Stub ALL env vars before any import — guarantees every conditional
@@ -27,6 +28,7 @@ beforeAll(() => {
27
28
  vi.stubEnv("OPEDD_BUYER_JWT", TEST_BUYER_JWT);
28
29
  vi.stubEnv("OPEDD_ACCESS_KEY", TEST_ACCESS_KEY);
29
30
  vi.stubEnv("OPEDD_API_KEY", TEST_API_KEY);
31
+ vi.stubEnv("OPEDD_PUB_BEARER", TEST_PUB_BEARER);
30
32
  vi.stubEnv("OPEDD_BUYER_EMAIL", TEST_BUYER_EMAIL);
31
33
  vi.stubEnv("OPEDD_API_URL", "https://api.opedd.com");
32
34
  });
@@ -257,15 +259,18 @@ describe("dispatchTool: stream_feed_ndjson (ACCESS_KEY gated)", () => {
257
259
 
258
260
  // ───────────────────────────── API_KEY-gated tool ─────────────────────────────
259
261
 
260
- describe("dispatchTool: list_publisher_content (API_KEY gated)", () => {
261
- it("happy path — sends /api?action=articles + X-API-Key header", async () => {
262
+ describe("dispatchTool: list_publisher_content (PUB_BEARER preferred; API_KEY fallback)", () => {
263
+ it("happy path — sends /api?action=articles + Authorization: Bearer canonical (v0.4.0)", async () => {
262
264
  const f = mockFetchOk({ success: true, data: { articles: [] } });
263
265
  const { dispatchTool } = await loadDispatcher();
264
266
  await dispatchTool("list_publisher_content", { limit: 20 });
265
267
  const call = f.mock.calls[0];
266
268
  expect(String(call[0])).toContain("/api?action=articles");
267
269
  const headers = (call[1] as RequestInit).headers as Record<string, string>;
268
- expect(headers["X-API-Key"]).toBe(TEST_API_KEY);
270
+ // v0.4.0: Bearer preferred when OPEDD_PUB_BEARER is set; X-API-Key
271
+ // legacy fallback only when PUB_BEARER unset.
272
+ expect(headers["Authorization"]).toBe("Bearer " + TEST_PUB_BEARER);
273
+ expect(headers["X-API-Key"]).toBeUndefined();
269
274
  });
270
275
 
271
276
  it("limit capped at 100", async () => {