opedd-mcp 0.7.0 → 0.9.0

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.
Files changed (3) hide show
  1. package/README.md +10 -8
  2. package/dist/index.js +120 -90
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -35,7 +35,7 @@ Then add it to your MCP host (see [Claude Desktop / Cursor / Windsurf setup](#cl
35
35
 
36
36
  ## Hosted endpoint — no install (mcp.opedd.com)
37
37
 
38
- Prefer zero-install? The same 17 tools are served hosted at **`https://mcp.opedd.com/mcp`** (Streamable HTTP). Auth is an `Authorization: Bearer` header with any Opedd key — public discovery tools need no auth at all.
38
+ Prefer zero-install? The same 19 tools are served hosted at **`https://mcp.opedd.com/mcp`** (Streamable HTTP). Auth is an `Authorization: Bearer` header with any Opedd key — public discovery tools need no auth at all.
39
39
 
40
40
  **Claude Messages API** (production agents):
41
41
 
@@ -82,18 +82,19 @@ Key routing: `opedd_sk_*` (legacy `opedd_pub_*`) → publisher tools · `opedd_b
82
82
 
83
83
  ## What it does
84
84
 
85
- Exposes up to 17 tools to any AI assistant (some are conditional on env vars):
85
+ Exposes up to 19 tools to any AI assistant (some are conditional on env vars):
86
86
 
87
87
  **Always available — discovery + per-article purchase + onboarding + rights signaling**
88
88
 
89
89
  | Tool | Description |
90
90
  |------|-------------|
91
+ | `search_content` | Full-text search across everything licensable (v0.8.0) — ranked matches with publisher, prices, word count and a description snippet; quotes for phrases, `-word` to exclude |
91
92
  | `lookup_content` | Look up an article by URL — returns title, publisher, pricing |
92
- | `purchase_license` | Buy a single-article license via Stripe — returns OP-XXXX-XXXX key. Requires `terms_accepted: true` (genuine buyer assent to opedd.com/terms; recorded with the licence) |
93
+ | `purchase_license` | Buy a Human republication licence for one article via Stripe — returns OP-XXXX-XXXX key. Requires `terms_accepted: true` (genuine buyer assent to opedd.com/terms; recorded with the licence) |
93
94
  | `verify_license` | Verify a license key — returns validity, article, publisher, blockchain proof |
94
95
  | `browse_registry` | Browse the public Opedd registry — lists issued LICENSES (use `publisher_directory` to browse publishers themselves) |
95
96
  | `publisher_directory` | Browse the public Opedd publisher catalog — paginated publishers with article counts + pricing + sample articles (primary buyer-discovery surface for AI labs) |
96
- | `purchase_enterprise_license` | Buy a bulk enterprise license covering multiple publishers (Phase 10) — returns Stripe `client_secret`. Requires `terms_accepted: true`; the current Master Services Agreement version label is recorded with the licence (the backend rejects superseded labels) |
97
+ | `place_licence_order` | Place a licence order for one or more publishers: AI answers (`monthly` full text, or `metered` pay-per-request snippets ≤300 words / 25%), AI training (`one_time`, back catalogue), client display (`monthly` × end clients). Each publisher is its own Schedule at its own price; returns the order, lines, `unavailable` publishers and `hosted_invoice_url`. Needs `OPEDD_BUYER_JWT` and `terms_accepted: true` |
97
98
  | `rsl_get` | Fetch a publisher's RSL Standard manifest — public discovery surface; `jsonld: true` returns CDSM Article 4(3) signed receipt (Phase 12 W1.1) |
98
99
  | `detect_platform` | Detect the content platform behind a URL — returns suggested onboarding workflow for Substack / Beehiiv / Ghost / Medium / Brevo / custom (Phase 12 W3.1) |
99
100
 
@@ -101,20 +102,21 @@ Exposes up to 17 tools to any AI assistant (some are conditional on env vars):
101
102
 
102
103
  | Tool | Description |
103
104
  |------|-------------|
104
- | `get_content` | Retrieve a licensed article — includes 7 Phase 11 M2 RAG metadata fields (author, language, word_count, content_hash, image_urls, canonical_url, tags) |
105
+ | `get_content` | Retrieve a licensed article — full text for AI answers monthly and client display orders, a snippet (≤300 words / 25%) for pay-per-request; includes 7 RAG metadata fields (author, language, word_count, content_hash, image_urls, canonical_url, tags) |
105
106
 
106
107
  **Requires `OPEDD_ACCESS_KEY` (ent_*) — buyer-side feed surfaces**
107
108
 
108
109
  | Tool | Description |
109
110
  |------|-------------|
110
- | `list_feed` | List articles from a buyer's licensed catalog with `since` delta-feed support (Phase 11 M5). Flat-fee scopes carry full `content_body`; metered (filtered-scope) keys get a discovery-only feed (`content_body: null`, `content_access: "metered_per_call"`) — fetch text via `get_content` (billed per call) |
111
- | `stream_feed_ndjson` | Bulk-export up to 1000 articles per call via NDJSON wire format (Phase 11 M3). Same per-scope content contract as `list_feed` — metered keys export metadata only |
111
+ | `list_feed` | List articles from a buyer's licensed catalog with `since` delta-feed support. AI training orders carry full `content_body` (back catalogue up to the order date, `content_access: "included"`); every other order is discovery-only (`content_body: null`, `content_access: "retrieval_per_article"` or `"metered_per_call"`) — fetch text via `get_content` |
112
+ | `stream_feed_ndjson` | Bulk-export up to 1000 articles per call via NDJSON wire format. Same content contract as `list_feed` — full text for AI training orders only |
112
113
 
113
114
  **Requires `OPEDD_BUYER_JWT` (Supabase JWT) — buyer account + audit + compliance + EU AI Act surfaces**
114
115
 
115
116
  | Tool | Description |
116
117
  |------|-------------|
117
118
  | `get_buyer_account` | Fetch buyer profile + masked API key list — buyer-dashboard mental model |
119
+ | `list_licence_orders` | The buyer's licence orders and lines; with `order_id`, each line's Schedule document and its SHA-256 (the fingerprint recorded on Tempo). Also works with an audit-scoped `OPEDD_BUYER_TOKEN` |
118
120
  | `get_audit_events` | Per-event audit ledger with Tempo on-chain attestation inclusion proofs inline (Phase 9.x + 10 M5) |
119
121
  | `get_compliance_dossier` | Procurement-defense compliance dossier mapping retrievals to license terms (Phase 11 M4) |
120
122
  | `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) |
@@ -158,7 +160,7 @@ Set environment variables to pre-configure the server:
158
160
  | `OPEDD_PAYMENT_METHOD_ID` | Recommended | Stripe `pm_...` ID — used for autonomous per-article purchasing |
159
161
  | `OPEDD_BUYER_TOKEN` | Optional | Buyer API token (`opedd_buyer_live_*` canonical; `opedd_buyer_test_*` for sandbox) — enables `get_content` |
160
162
  | `OPEDD_ACCESS_KEY` | Optional | Enterprise access key (`ent_*`) — enables `list_feed` + `stream_feed_ndjson` |
161
- | `OPEDD_BUYER_JWT` | Optional | Supabase session JWT from the buyer portal — enables `get_buyer_account` + `get_audit_events` + `get_compliance_dossier` + `article_53_attestation` |
163
+ | `OPEDD_BUYER_JWT` | Optional | Supabase session JWT from the buyer portal — enables `place_licence_order` + `list_licence_orders` + `get_buyer_account` + `get_audit_events` + `get_compliance_dossier` + `article_53_attestation` |
162
164
  | `OPEDD_PUB_BEARER` | Optional | Publisher API key (`opedd_sk_<32-hex>`, canonical since 2026-08-27; older `opedd_pub_<32-hex>` keys keep working) — from the Opedd dashboard, Settings → Developers. Enables `list_publisher_content` (+ `include_body`) and `push_content`. |
163
165
  | `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`. |
164
166
  | `OPEDD_API_URL` | Optional | Override the API base URL (default: Opedd production) |
package/dist/index.js CHANGED
@@ -148,7 +148,7 @@ function auditCred(creds) {
148
148
  return creds.buyerToken || creds.buyerJwt;
149
149
  }
150
150
  const HOW_ACCESS_KEY = "Enterprise access keys (ent_*) are delivered when an enterprise licence is issued — start at https://opedd.com/enterprise " +
151
- "or purchase programmatically with the purchase_enterprise_license tool.";
151
+ "or place a licence order with the place_licence_order tool.";
152
152
  const HOW_PUB_BEARER = "Publisher keys (opedd_sk_*, legacy opedd_pub_* still valid) come from the publisher dashboard: sign up free at https://opedd.com, then Settings → API keys.";
153
153
  // ─── Tool definitions ─────────────────────────────────────────────────────────
154
154
  function buildTools(has) {
@@ -170,13 +170,30 @@ function buildTools(has) {
170
170
  },
171
171
  },
172
172
  },
173
+ {
174
+ name: "search_content",
175
+ description: "Full-text search across everything licensable on Opedd (public, no key needed). " +
176
+ "Ask in plain words or use quotes for phrases and -word to exclude. Returns ranked matches with the publisher, " +
177
+ "prices, word count and a short description snippet (never the body). Use it to answer 'does Opedd have coverage on X', " +
178
+ "then lookup_content or get_content for a specific article.",
179
+ inputSchema: {
180
+ type: "object",
181
+ required: ["query"],
182
+ properties: {
183
+ query: { type: "string", description: "Search words (max 200 characters)." },
184
+ limit: { type: "number", description: "Max results (default 20, max 50)." },
185
+ publisher: { type: "string", description: "Optional publisher slug to search within." },
186
+ },
187
+ },
188
+ },
173
189
  {
174
190
  name: "purchase_license",
175
191
  description: "Purchase a content license from the Opedd protocol using a Stripe payment method. " +
176
192
  "Returns a license key (format: OP-XXXX-XXXX) and a certificate URL. " +
177
193
  "The buyer receives a Handshake Email with their license key. " +
178
194
  "Set OPEDD_BUYER_EMAIL and OPEDD_PAYMENT_METHOD_ID env vars to avoid passing them on every call. " +
179
- "License types: 'human' = republication rights, 'ai' = training dataset rights, 'ai_inference' = inference/RAG rights.",
195
+ "Single-article purchases are Human republication licences only (license_type 'human'). " +
196
+ "AI licences (AI answers, AI training, client display) are bought as licence orders: see place_licence_order.",
180
197
  inputSchema: {
181
198
  type: "object",
182
199
  required: ["license_type", "terms_accepted"],
@@ -196,8 +213,8 @@ function buildTools(has) {
196
213
  },
197
214
  license_type: {
198
215
  type: "string",
199
- enum: ["human", "ai", "ai_inference"],
200
- description: "human = republication/editorial rights, ai = training dataset rights, ai_inference = inference/RAG rights",
216
+ enum: ["human"],
217
+ description: "human = Human republication (editorial republication of one article)",
201
218
  },
202
219
  buyer_email: {
203
220
  type: "string",
@@ -355,61 +372,27 @@ function buildTools(has) {
355
372
  },
356
373
  // ─── Phase 10 + 11 buyer-side surfaces (M6.4) ─────────────────────────────
357
374
  {
358
- name: "purchase_enterprise_license",
359
- description: "Purchase a bulk enterprise license covering multiple publishers (Phase 10). " +
360
- "Returns a Stripe client_secret for payment completion + the enterprise_license_id. " +
361
- "After payment, an ent_* access key is emailed to buyer_email. " +
362
- "Scopes: 'custom' (pass-through publisher_ids), 'platform_wide' (auto-resolve all opted-in publishers), 'filtered' (Phase 10 filter_rules). " +
363
- "License tiers: 'rag' (= ai_retrieval), 'training' (= ai_training, flat-fee not metered), 'inference' (= ai_retrieval), 'full_ai' (writes both retrieval + training records). " +
364
- "The buyer must accept the Opedd Master Services Agreement (opedd.com/terms) before purchase — set terms_accepted=true to record it.",
375
+ name: "place_licence_order",
376
+ description: "Place a licence order with Opedd for one or more publishers. One order = one licence and one billing mode; " +
377
+ "each publisher becomes its own Schedule priced from that publisher's settings (publishers that do not offer it are " +
378
+ "returned in `unavailable` and never charged). Licences: 'enterprise' (AI answers: 'monthly' full text per article, or " +
379
+ "'metered' pay-per-request snippets of up to 300 words or 25% of the article), 'training' (AI training of the back " +
380
+ "catalogue up to the order date: 'one_time', bulk export via stream_feed_ndjson), 'display' (client display: 'monthly', " +
381
+ "quantity = number of end clients). Returns the order, its lines, the access key (works once paid) and " +
382
+ "hosted_invoice_url — the buyer (your principal) pays there. Requires a buyer session (OPEDD_BUYER_JWT) and " +
383
+ "terms_accepted=true after the buyer has accepted the Opedd Master Services Agreement (opedd.com/terms).",
365
384
  inputSchema: {
366
385
  type: "object",
367
- required: ["publisher_ids", "buyer_email", "buyer_org", "terms_accepted"],
386
+ required: ["licence", "billing_mode", "publisher_ids", "terms_accepted"],
368
387
  properties: {
388
+ licence: { type: "string", enum: ["enterprise", "training", "display"], description: "enterprise = AI answers, training = AI training, display = client display" },
389
+ billing_mode: { type: "string", enum: ["monthly", "metered", "one_time"], description: "enterprise: monthly | metered; training: one_time; display: monthly" },
390
+ publisher_ids: { type: "array", items: { type: "string" }, description: "Publisher UUIDs (1-500; at most 18 for metered)" },
391
+ quantity: { type: "number", description: "display only: number of end clients (1-500)" },
369
392
  terms_accepted: {
370
393
  type: "boolean",
371
- description: "REQUIRED. Set true only after the buyer (your principal) has accepted the Opedd Master Services Agreement at opedd.com/terms. " +
372
- "The current MSA version label is recorded with the licence; purchases without genuine acceptance are rejected (HTTP 400).",
373
- },
374
- publisher_ids: {
375
- type: "array",
376
- items: { type: "string" },
377
- description: "Array of publisher UUIDs. Required for scope='custom'; ignored for platform_wide/filtered (resolved server-side).",
378
- },
379
- buyer_email: {
380
- type: "string",
381
- description: "Email to deliver the access key after payment",
382
- },
383
- buyer_org: {
384
- type: "string",
385
- description: "Buyer organization name (for billing + audit ledger)",
386
- },
387
- billing_type: {
388
- type: "string",
389
- enum: ["annual", "monthly", "annual_plus_monthly"],
390
- description: "Billing cadence (default: annual)",
391
- },
392
- license_tier: {
393
- type: "string",
394
- enum: ["rag", "training", "inference", "full_ai"],
395
- description: "License tier (default: rag)",
396
- },
397
- duration_months: {
398
- type: "number",
399
- description: "License duration in months (default: 12)",
400
- },
401
- scope: {
402
- type: "string",
403
- enum: ["custom", "platform_wide", "filtered"],
404
- description: "Coverage scope (default: custom)",
405
- },
406
- filter_rules: {
407
- type: "object",
408
- description: "Required when scope='filtered'. See Phase 10 docs for shape: excluded_publisher_ids / direct_license_carveouts / categories / max_price_per_event.",
409
- },
410
- buyer_webhook_url: {
411
- type: "string",
412
- description: "Optional HMAC-signed webhook for content.published events on covered publishers",
394
+ description: "REQUIRED. Set true only after the buyer has accepted the Opedd Master Services Agreement at opedd.com/terms. " +
395
+ "The current version label is recorded with the order; orders without genuine acceptance are rejected.",
413
396
  },
414
397
  },
415
398
  },
@@ -421,7 +404,9 @@ function buildTools(has) {
421
404
  name: "get_content",
422
405
  description: "Retrieve the full body of a licensed article using a buyer API token (opedd_buyer_live_* canonical; opedd_buyer_test_* for sandbox). " +
423
406
  "Requires OPEDD_BUYER_TOKEN env var (create one at opedd.com/licenses after purchasing). " +
424
- "Works for per-article licenses (token scoped to that article) and archive licenses (token covers all publisher content). " +
407
+ "Works for per-article Human republication licences (token scoped to that article) and licence orders: AI answers " +
408
+ "monthly and client display return full text; AI answers pay-per-request always returns a snippet (up to 300 words " +
409
+ "or 25% of the article, whatever delivery_mode is asked). Articles the publisher stopped licensing answer 403 ARTICLE_EXCLUDED. " +
425
410
  "The publisher must have content delivery enabled and must have pushed content for the article. " +
426
411
  "Phase 11 M2 RAG-extended shape: response includes 7 RAG-essential metadata fields — author, language, word_count, content_hash, image_urls, canonical_url, tags. " +
427
412
  "On pre-2026-05-14 historical articles, optional fields (author/language/image_urls/canonical_url/tags) may be NULL. " +
@@ -447,9 +432,10 @@ function buildTools(has) {
447
432
  TOOLS.push({
448
433
  name: "list_feed",
449
434
  description: "List articles from a buyer's licensed catalog via GET /enterprise-license (Phase 10 + 11). " +
450
- "Content contract: flat-fee scopes (custom/platform_wide) include full content_body; METERED " +
451
- "(filtered-scope) keys get a discovery-only feed — content_body is null and content_access is " +
452
- "'metered_per_call'; fetch article text via get_content (each retrieval is billed). " +
435
+ "Content contract: AI training orders include full content_body for the back catalogue up to the order date " +
436
+ "(content_access 'included'). Every other order is discovery-only (content_body null): AI answers monthly and " +
437
+ "client display fetch text per article via get_content (content_access 'retrieval_per_article'); pay-per-request " +
438
+ "fetches billed snippets via get_content (content_access 'metered_per_call'). " +
453
439
  "Returns JSON-format response with paginated articles. " +
454
440
  "Use `since` (ISO 8601) for delta-feed polling — only articles published after the timestamp. " +
455
441
  "Use `cursor` for pagination across pages. " +
@@ -477,8 +463,8 @@ function buildTools(has) {
477
463
  name: "stream_feed_ndjson",
478
464
  description: "Bulk-export a buyer's licensed catalog via GET /enterprise-license?format=ndjson (Phase 11 M3). " +
479
465
  "Returns up to 1000 articles per call (collected from line-delimited JSON wire format). " +
480
- "Same per-scope content contract as list_feed: METERED (filtered-scope) keys export metadata only " +
481
- "(content_body null, content_access 'metered_per_call') — use get_content for article text. " +
466
+ "Same content contract as list_feed: full text for AI training orders only; every other order exports " +
467
+ "metadata (content_body null) — use get_content for article text. " +
482
468
  "Each article emits one usage_records row (analytics-only sentinel 'bulk-export:<request_id>:<article_id>' — not metered-billable per the revenue-model bifurcation invariant). " +
483
469
  "Use `since` (ISO 8601) for delta-feed. Use `cursor` to paginate beyond 1000. " +
484
470
  "Backend supports 5000 articles per call; the MCP cap is 1000 for transport reasonability. " +
@@ -508,6 +494,21 @@ function buildTools(has) {
508
494
  // key (OPEDD_BUYER_TOKEN) works on 3 of these 4; get_buyer_account remains
509
495
  // JWT-only (its endpoint mints keys — a key must never mint keys).
510
496
  if (has.buyerJwt || has.buyerToken) {
497
+ TOOLS.push({
498
+ name: "list_licence_orders",
499
+ description: "List the buyer's licence orders, newest first, each with its lines (publisher, licence, unit, price, quantity, status). " +
500
+ "Pass order_id to read one order: each line then carries its Schedule document text and SHA-256 — the licence " +
501
+ "fingerprint recorded on the Tempo blockchain when the line was issued. Read-only. Requires a buyer API key with " +
502
+ "the 'audit' scope (OPEDD_BUYER_TOKEN) or a buyer session (OPEDD_BUYER_JWT).",
503
+ inputSchema: {
504
+ type: "object",
505
+ properties: {
506
+ order_id: { type: "string", description: "Read one order (UUID) including Schedule documents" },
507
+ cursor: { type: "string", description: "Opaque cursor from the prior response's pagination.next_cursor" },
508
+ limit: { type: "number", description: "Orders per page (default 20, max 100)" },
509
+ },
510
+ },
511
+ });
511
512
  TOOLS.push({
512
513
  name: "get_audit_events",
513
514
  description: "Browse per-event audit rows for the authenticated buyer via GET /buyer-audit (Phase 9.x). " +
@@ -768,6 +769,22 @@ export async function dispatchTool(name, args, creds = ENV_CREDS) {
768
769
  const data = await opeddFetch(creds, `/lookup-article?url=${encodeURIComponent(url)}`);
769
770
  return ok(data);
770
771
  }
772
+ // ── search_content ─────────────────────────────────────────────────────
773
+ case "search_content": {
774
+ const { query, limit, publisher } = args;
775
+ const q = (query ?? "").replace(/\s+/g, " ").trim();
776
+ if (!q)
777
+ return err("query is required");
778
+ if (q.length > 200)
779
+ return err("query must be at most 200 characters");
780
+ const params = new URLSearchParams({ action: "search", q });
781
+ if (typeof limit === "number" && Number.isFinite(limit))
782
+ params.set("limit", String(Math.min(Math.max(Math.trunc(limit), 1), 50)));
783
+ if (typeof publisher === "string" && publisher.trim())
784
+ params.set("publisher", publisher.trim());
785
+ const envelope = (await opeddFetch(creds, `/api?${params.toString()}`));
786
+ return ok(envelope.data ?? envelope);
787
+ }
771
788
  // ── purchase_license ───────────────────────────────────────────────────
772
789
  case "purchase_license": {
773
790
  const { article_url, article_id, license_type, buyer_email: argEmail, buyer_name, buyer_organization, intended_use, payment_method_id: argPm, } = args;
@@ -875,41 +892,54 @@ export async function dispatchTool(name, args, creds = ENV_CREDS) {
875
892
  const data = await opeddFetch(creds, `/rsl-manifest?publisher_id=${encodeURIComponent(publisher_id)}`, { headers: { Accept: accept } });
876
893
  return ok(data);
877
894
  }
878
- // ── purchase_enterprise_license (Phase 10) ─────────────────────────────
879
- case "purchase_enterprise_license": {
880
- const { publisher_ids, buyer_email: pelEmail, buyer_org, billing_type = "annual", license_tier = "rag", duration_months = 12, scope = "custom", filter_rules, buyer_webhook_url, terms_accepted, } = args;
881
- if (!Array.isArray(publisher_ids) || publisher_ids.length === 0) {
882
- if (scope === "custom") {
883
- return err("publisher_ids array is required for scope='custom'");
884
- }
895
+ // ── place_licence_order (licensing MVP, backend PR-B1) ─────────────────
896
+ // POST /enterprise-license requires a buyer SESSION since 2026-09-17
897
+ // (API keys are refused on money endpoints): the order is placed with
898
+ // OPEDD_BUYER_JWT and the buyer pays the hosted invoice.
899
+ case "place_licence_order": {
900
+ if (!creds.buyerJwt) {
901
+ return credErr("A buyer session is required to place an order", "OPEDD_BUYER_JWT", HOW_BUYER_JWT);
885
902
  }
886
- if (!pelEmail)
887
- return err("buyer_email is required");
888
- if (!buyer_org)
889
- return err("buyer_org is required");
903
+ const { licence, billing_mode, publisher_ids, quantity, terms_accepted } = args;
904
+ if (!licence || !billing_mode)
905
+ return err("licence and billing_mode are required");
906
+ if (!Array.isArray(publisher_ids) || publisher_ids.length === 0)
907
+ return err("publisher_ids must be a non-empty array");
890
908
  if (terms_accepted !== true) {
891
- return err("terms_accepted must be true — confirm with the buyer that they accept the Opedd Master Services Agreement (opedd.com/terms) before purchasing.");
909
+ return err("terms_accepted must be true — confirm with the buyer that they accept the Opedd Master Services Agreement (opedd.com/terms) before ordering.");
892
910
  }
893
- const body = {
894
- publisher_ids: publisher_ids ?? [],
895
- buyer_email: pelEmail,
896
- buyer_org,
897
- billing_type,
898
- license_tier,
899
- duration_months,
900
- scope,
901
- // Genuine assent moment: the agent asserted terms_accepted=true just
902
- // now. The backend requires the MSA version label (fail-closed since
903
- // 2026-07-31) and rejects superseded labels — on an MSA version bump
904
- // this constant must be updated (new package release) to match
905
- // opedd-backend _shared/msa-version.ts CURRENT_MSA_VERSION.
906
- terms_version: CURRENT_MSA_VERSION,
907
- ...(filter_rules ? { filter_rules } : {}),
908
- ...(buyer_webhook_url ? { buyer_webhook_url } : {}),
909
- };
910
911
  const data = await opeddFetch(creds, "/enterprise-license", {
911
912
  method: "POST",
912
- body: JSON.stringify(body),
913
+ headers: { Authorization: `Bearer ${creds.buyerJwt}` },
914
+ body: JSON.stringify({
915
+ licence,
916
+ billing_mode,
917
+ publisher_ids,
918
+ ...(quantity !== undefined ? { quantity } : {}),
919
+ // Genuine assent moment: the agent asserted terms_accepted=true just
920
+ // now. On an MSA version bump this constant must be updated (new
921
+ // package release) to match opedd-backend _shared/msa-version.ts.
922
+ terms_version: CURRENT_MSA_VERSION,
923
+ }),
924
+ });
925
+ return ok(data);
926
+ }
927
+ // ── list_licence_orders (GET /buyer-orders) ──────────────────────────
928
+ case "list_licence_orders": {
929
+ if (!auditCred(creds)) {
930
+ return credErr("Buyer credentials are required for this tool", "OPEDD_BUYER_TOKEN (audit-scoped key) or OPEDD_BUYER_JWT", HOW_AUDIT_CRED);
931
+ }
932
+ const { order_id, cursor, limit } = args;
933
+ const params = new URLSearchParams();
934
+ if (order_id)
935
+ params.set("order_id", order_id);
936
+ if (cursor)
937
+ params.set("cursor", cursor);
938
+ if (limit !== undefined)
939
+ params.set("limit", String(Math.min(Math.max(Number(limit) || 20, 1), 100)));
940
+ const qs = params.toString();
941
+ const data = await opeddFetch(creds, `/buyer-orders${qs ? `?${qs}` : ""}`, {
942
+ headers: { Authorization: `Bearer ${auditCred(creds)}` },
913
943
  });
914
944
  return ok(data);
915
945
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opedd-mcp",
3
- "version": "0.7.0",
4
- "description": "Licensed, rights-cleared content for AI agents \u2014 verifiable license keys, on-chain proof, and EU AI Act attestation. The MCP alternative to unlicensed scraping for RAG and AI search.",
3
+ "version": "0.9.0",
4
+ "description": "Licensed, rights-cleared content for AI agents — verifiable license keys, on-chain proof, and EU AI Act attestation. The MCP alternative to unlicensed scraping for RAG and AI search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "mcpName": "com.opedd/opedd-mcp",
@@ -60,4 +60,4 @@
60
60
  "overrides": {
61
61
  "esbuild": "^0.28.1"
62
62
  }
63
- }
63
+ }