opedd-mcp 0.6.12 → 0.6.14
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 +5 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +52 -9
- package/dist/telemetry.d.ts +1 -0
- package/dist/telemetry.js +25 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,11 +89,11 @@ Exposes up to 17 tools to any AI assistant (some are conditional on env vars):
|
|
|
89
89
|
| Tool | Description |
|
|
90
90
|
|------|-------------|
|
|
91
91
|
| `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 |
|
|
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
93
|
| `verify_license` | Verify a license key — returns validity, article, publisher, blockchain proof |
|
|
94
94
|
| `browse_registry` | Browse the public Opedd registry — lists issued LICENSES (use `publisher_directory` to browse publishers themselves) |
|
|
95
95
|
| `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` |
|
|
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
97
|
| `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
98
|
| `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
99
|
|
|
@@ -107,8 +107,8 @@ Exposes up to 17 tools to any AI assistant (some are conditional on env vars):
|
|
|
107
107
|
|
|
108
108
|
| Tool | Description |
|
|
109
109
|
|------|-------------|
|
|
110
|
-
| `list_feed` | List articles from a buyer's licensed catalog with `since` delta-feed support (Phase 11 M5) |
|
|
111
|
-
| `stream_feed_ndjson` | Bulk-export up to 1000 articles per call via NDJSON wire format (Phase 11 M3) |
|
|
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 |
|
|
112
112
|
|
|
113
113
|
**Requires `OPEDD_BUYER_JWT` (Supabase JWT) — buyer account + audit + compliance + EU AI Act surfaces**
|
|
114
114
|
|
|
@@ -266,7 +266,7 @@ npm run build # compiles to dist/
|
|
|
266
266
|
| | Opedd | Generic search APIs |
|
|
267
267
|
|---|---|---|
|
|
268
268
|
| **What you get** | Licensed content + license key + on-chain proof — all in one API call | Scraped web content, no rights |
|
|
269
|
-
| **Content delivery** | Full article text
|
|
269
|
+
| **Content delivery** | Full article text via API (JSON) — bulk on flat-fee licenses, per-call (metered) on filtered licenses; real-time feed via webhooks | Scraped snippets or full-page dumps |
|
|
270
270
|
| **Content quality** | Curated publisher content (niche B2B newsletters, expert analysis) | Whatever's on the open web |
|
|
271
271
|
| **Rights** | Verifiable license key per article, publisher-authorized | No rights clearance |
|
|
272
272
|
| **Proof** | On-chain (Tempo blockchain) — independently verifiable | None |
|
package/dist/index.d.ts
CHANGED
|
@@ -27,5 +27,6 @@ type ToolResult = {
|
|
|
27
27
|
};
|
|
28
28
|
export declare const TOOLS: Tool[];
|
|
29
29
|
export declare const ALL_TOOLS: Tool[];
|
|
30
|
+
export declare const SERVER_INSTRUCTIONS: string;
|
|
30
31
|
export declare function dispatchTool(name: string, args: Record<string, unknown>, creds?: Credentials): Promise<ToolResult>;
|
|
31
32
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -118,6 +118,27 @@ function ok(data) {
|
|
|
118
118
|
function err(msg) {
|
|
119
119
|
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
120
120
|
}
|
|
121
|
+
// ─── Credential refusals are the conversion surface ──────────────────────────
|
|
122
|
+
// (2026-08-26) In the week before this landed, 19 distinct agents worldwide
|
|
123
|
+
// succeeded on browse_registry and then hit a credential gate whose entire
|
|
124
|
+
// message was "env var is required" — no next action, funnel over. The AUTH
|
|
125
|
+
// MODEL IS UNCHANGED by this block: same gates, same keys, nothing loosened.
|
|
126
|
+
// Only the refusal text changed: what's missing, how to supply it on BOTH
|
|
127
|
+
// channels (env var locally, Authorization header on the hosted gateway),
|
|
128
|
+
// where credentials come from, and which tools need none.
|
|
129
|
+
const NO_AUTH_TOOLS = "lookup_content, publisher_directory, verify_license, browse_registry, rsl_get, detect_platform";
|
|
130
|
+
function credErr(missing, envVar, howToGet) {
|
|
131
|
+
return err(`${missing} — none was provided. ` +
|
|
132
|
+
`Supply it as the ${envVar} env var (local/stdio install) or as an "Authorization: Bearer <value>" header (hosted gateway, https://mcp.opedd.com/mcp). ` +
|
|
133
|
+
`${howToGet} ` +
|
|
134
|
+
`These tools need no credentials at all: ${NO_AUTH_TOOLS}. Docs: https://docs.opedd.com`);
|
|
135
|
+
}
|
|
136
|
+
const HOW_BUYER_JWT = "Buyer signup is free and takes ~2 minutes: https://opedd.com/buyer/signup (an API key is issued instantly at signup). " +
|
|
137
|
+
"This tool authenticates with your buyer session JWT: sign in at https://opedd.com/buyer and use the Supabase session access token. " +
|
|
138
|
+
"Blocked? hello@opedd.com.";
|
|
139
|
+
const HOW_ACCESS_KEY = "Enterprise access keys (ent_*) are delivered when an enterprise licence is issued — start at https://opedd.com/enterprise " +
|
|
140
|
+
"or purchase programmatically with the purchase_enterprise_license tool.";
|
|
141
|
+
const HOW_PUB_BEARER = "Publisher keys (opedd_pub_*) come from the publisher dashboard: sign up free at https://opedd.com, then Settings → API keys.";
|
|
121
142
|
// ─── Tool definitions ─────────────────────────────────────────────────────────
|
|
122
143
|
function buildTools(has) {
|
|
123
144
|
const TOOLS = [
|
|
@@ -415,6 +436,9 @@ function buildTools(has) {
|
|
|
415
436
|
TOOLS.push({
|
|
416
437
|
name: "list_feed",
|
|
417
438
|
description: "List articles from a buyer's licensed catalog via GET /enterprise-license (Phase 10 + 11). " +
|
|
439
|
+
"Content contract: flat-fee scopes (custom/platform_wide) include full content_body; METERED " +
|
|
440
|
+
"(filtered-scope) keys get a discovery-only feed — content_body is null and content_access is " +
|
|
441
|
+
"'metered_per_call'; fetch article text via get_content (each retrieval is billed). " +
|
|
418
442
|
"Returns JSON-format response with paginated articles. " +
|
|
419
443
|
"Use `since` (ISO 8601) for delta-feed polling — only articles published after the timestamp. " +
|
|
420
444
|
"Use `cursor` for pagination across pages. " +
|
|
@@ -442,6 +466,8 @@ function buildTools(has) {
|
|
|
442
466
|
name: "stream_feed_ndjson",
|
|
443
467
|
description: "Bulk-export a buyer's licensed catalog via GET /enterprise-license?format=ndjson (Phase 11 M3). " +
|
|
444
468
|
"Returns up to 1000 articles per call (collected from line-delimited JSON wire format). " +
|
|
469
|
+
"Same per-scope content contract as list_feed: METERED (filtered-scope) keys export metadata only " +
|
|
470
|
+
"(content_body null, content_access 'metered_per_call') — use get_content for article text. " +
|
|
445
471
|
"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). " +
|
|
446
472
|
"Use `since` (ISO 8601) for delta-feed. Use `cursor` to paginate beyond 1000. " +
|
|
447
473
|
"Backend supports 5000 articles per call; the MCP cap is 1000 for transport reasonability. " +
|
|
@@ -665,7 +691,24 @@ export const ALL_TOOLS = buildTools({
|
|
|
665
691
|
buyerToken: true, accessKey: true, buyerJwt: true, pubBearer: true,
|
|
666
692
|
});
|
|
667
693
|
// ─── MCP Server ───────────────────────────────────────────────────────────────
|
|
668
|
-
|
|
694
|
+
// ─── Handshake instructions ──────────────────────────────────────────────────
|
|
695
|
+
// The MCP `instructions` field is the one string every connecting agent reads
|
|
696
|
+
// at initialize — it was empty until 2026-08-26 while the pitch lived only in
|
|
697
|
+
// registry metadata agents never see at runtime. Exported so the hosted
|
|
698
|
+
// gateway (opedd-mcp-gateway) serves the identical text.
|
|
699
|
+
export const SERVER_INSTRUCTIONS = "Opedd is the licensing rail between expert publishers and AI products: discover, purchase, verify and " +
|
|
700
|
+
"retrieve rights-cleared expert content — one verifiable license key per article, Tempo on-chain " +
|
|
701
|
+
"attestation, and EU AI Act Article 53 compliance artifacts (audit ledger + dossiers). Use it instead of " +
|
|
702
|
+
"unlicensed scraping for RAG, AI search, and training corpora.\n\n" +
|
|
703
|
+
`No credentials needed: ${NO_AUTH_TOOLS}.\n` +
|
|
704
|
+
"Buyer tools (purchase, account, audit/compliance): free buyer signup at https://opedd.com/buyer/signup " +
|
|
705
|
+
"(~2 min; an API key is issued instantly). Publisher tools (content push): free publisher signup at " +
|
|
706
|
+
"https://opedd.com, key under Settings → API keys.\n" +
|
|
707
|
+
"Supply credentials as env vars (local/stdio) or an Authorization: Bearer header (hosted gateway, " +
|
|
708
|
+
"https://mcp.opedd.com/mcp).\n" +
|
|
709
|
+
"EU CDSM Art. 4(3) / TDM-reservation queries: rsl_get. Compliance evidence: get_compliance_dossier. " +
|
|
710
|
+
"Docs: https://docs.opedd.com";
|
|
711
|
+
const server = new Server({ name: "opedd-mcp", version: SERVER_VERSION }, { capabilities: { tools: {} }, instructions: SERVER_INSTRUCTIONS });
|
|
669
712
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
670
713
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
671
714
|
// Anonymous usage telemetry — captures only the tool name, duration, and
|
|
@@ -845,7 +888,7 @@ export async function dispatchTool(name, args, creds = ENV_CREDS) {
|
|
|
845
888
|
// ── list_feed (Phase 10 + 11) ──────────────────────────────────────────
|
|
846
889
|
case "list_feed": {
|
|
847
890
|
if (!creds.accessKey) {
|
|
848
|
-
return
|
|
891
|
+
return credErr("An enterprise access key (ent_*) is required for this tool", "OPEDD_ACCESS_KEY", HOW_ACCESS_KEY);
|
|
849
892
|
}
|
|
850
893
|
const { since, cursor, limit = 50 } = args;
|
|
851
894
|
const params = new URLSearchParams({
|
|
@@ -866,7 +909,7 @@ export async function dispatchTool(name, args, creds = ENV_CREDS) {
|
|
|
866
909
|
// ── stream_feed_ndjson (Phase 11 M3) ───────────────────────────────────
|
|
867
910
|
case "stream_feed_ndjson": {
|
|
868
911
|
if (!creds.accessKey) {
|
|
869
|
-
return
|
|
912
|
+
return credErr("An enterprise access key (ent_*) is required for this tool", "OPEDD_ACCESS_KEY", HOW_ACCESS_KEY);
|
|
870
913
|
}
|
|
871
914
|
const { since, cursor, limit = 200 } = args;
|
|
872
915
|
const params = new URLSearchParams({
|
|
@@ -887,7 +930,7 @@ export async function dispatchTool(name, args, creds = ENV_CREDS) {
|
|
|
887
930
|
// ── get_audit_events (Phase 9.x + 10 M5 attestation) ───────────────────
|
|
888
931
|
case "get_audit_events": {
|
|
889
932
|
if (!creds.buyerJwt) {
|
|
890
|
-
return
|
|
933
|
+
return credErr("A buyer session JWT is required for this tool", "OPEDD_BUYER_JWT", HOW_BUYER_JWT);
|
|
891
934
|
}
|
|
892
935
|
const { from, to, event_type, cursor, limit = 50 } = args;
|
|
893
936
|
const params = new URLSearchParams({
|
|
@@ -909,7 +952,7 @@ export async function dispatchTool(name, args, creds = ENV_CREDS) {
|
|
|
909
952
|
// ── get_buyer_account (buyer profile + masked key list) ────────────────
|
|
910
953
|
case "get_buyer_account": {
|
|
911
954
|
if (!creds.buyerJwt) {
|
|
912
|
-
return
|
|
955
|
+
return credErr("A buyer session JWT is required for this tool", "OPEDD_BUYER_JWT", HOW_BUYER_JWT);
|
|
913
956
|
}
|
|
914
957
|
const data = await opeddFetch(creds, "/buyer-account", {
|
|
915
958
|
headers: { Authorization: `Bearer ${creds.buyerJwt}` },
|
|
@@ -919,7 +962,7 @@ export async function dispatchTool(name, args, creds = ENV_CREDS) {
|
|
|
919
962
|
// ── article_53_attestation (Phase 12 Wave 1 W1.4) ──────────────────────
|
|
920
963
|
case "article_53_attestation": {
|
|
921
964
|
if (!creds.buyerJwt) {
|
|
922
|
-
return
|
|
965
|
+
return credErr("A buyer session JWT is required for this tool", "OPEDD_BUYER_JWT", HOW_BUYER_JWT);
|
|
923
966
|
}
|
|
924
967
|
const { license_id, content_id, window_start, window_end } = args;
|
|
925
968
|
if (!license_id)
|
|
@@ -942,7 +985,7 @@ export async function dispatchTool(name, args, creds = ENV_CREDS) {
|
|
|
942
985
|
// ── get_compliance_dossier (Phase 11 M4) ───────────────────────────────
|
|
943
986
|
case "get_compliance_dossier": {
|
|
944
987
|
if (!creds.buyerJwt) {
|
|
945
|
-
return
|
|
988
|
+
return credErr("A buyer session JWT is required for this tool", "OPEDD_BUYER_JWT", HOW_BUYER_JWT);
|
|
946
989
|
}
|
|
947
990
|
const { from, to, cursor } = args;
|
|
948
991
|
if (!from)
|
|
@@ -964,7 +1007,7 @@ export async function dispatchTool(name, args, creds = ENV_CREDS) {
|
|
|
964
1007
|
// ── list_publisher_content ─────────────────────────────────────────────
|
|
965
1008
|
case "list_publisher_content": {
|
|
966
1009
|
if (!creds.pubBearer && !creds.apiKey) {
|
|
967
|
-
return
|
|
1010
|
+
return credErr("A publisher key (opedd_pub_*) is required for this tool", "OPEDD_PUB_BEARER", HOW_PUB_BEARER);
|
|
968
1011
|
}
|
|
969
1012
|
const { limit = 20, type, offset = 0 } = args;
|
|
970
1013
|
const params = new URLSearchParams({ action: "articles" });
|
|
@@ -978,7 +1021,7 @@ export async function dispatchTool(name, args, creds = ENV_CREDS) {
|
|
|
978
1021
|
// ── push_content ───────────────────────────────────────────────────────
|
|
979
1022
|
case "push_content": {
|
|
980
1023
|
if (!creds.pubBearer && !creds.apiKey) {
|
|
981
|
-
return
|
|
1024
|
+
return credErr("A publisher key (opedd_pub_*) is required for this tool", "OPEDD_PUB_BEARER", HOW_PUB_BEARER);
|
|
982
1025
|
}
|
|
983
1026
|
const { articles } = args;
|
|
984
1027
|
if (!Array.isArray(articles) || articles.length === 0) {
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -8,4 +8,5 @@ export declare function isTelemetryEnabled(): boolean;
|
|
|
8
8
|
export declare function toolCallProperties(tool: string, durationMs: number, ok: boolean, extra?: Record<string, string | number | boolean>): Record<string, string | number | boolean>;
|
|
9
9
|
export declare function initTelemetry(): void;
|
|
10
10
|
export declare function captureToolCall(tool: string, durationMs: number, ok: boolean, extra?: Record<string, string | number | boolean>): void;
|
|
11
|
+
export declare function captureClientConnect(clientName: string, clientVersion: string, extra?: Record<string, string | number | boolean>): void;
|
|
11
12
|
export declare function shutdownTelemetry(): Promise<void>;
|
package/dist/telemetry.js
CHANGED
|
@@ -87,6 +87,31 @@ extra) {
|
|
|
87
87
|
/* telemetry must never affect a tool call */
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
+
// Client-framework identification (2026-08-26). The MCP initialize request
|
|
91
|
+
// carries the client's SELF-DECLARED name/version (e.g. "claude-desktop",
|
|
92
|
+
// "cursor", a company's custom agent) — the only identity signal the
|
|
93
|
+
// protocol offers, and we were discarding it while trying to identify
|
|
94
|
+
// callers by city. Same privacy posture as tool calls: these two strings
|
|
95
|
+
// plus the caller-supplied extra (geo/channel) only — no params, PII, keys.
|
|
96
|
+
export function captureClientConnect(clientName, clientVersion, extra) {
|
|
97
|
+
if (!client)
|
|
98
|
+
return;
|
|
99
|
+
try {
|
|
100
|
+
client.capture({
|
|
101
|
+
distinctId: sessionId,
|
|
102
|
+
event: "mcp_client_connect",
|
|
103
|
+
properties: {
|
|
104
|
+
...(extra ?? {}),
|
|
105
|
+
client_name: clientName.slice(0, 120),
|
|
106
|
+
client_version: clientVersion.slice(0, 40),
|
|
107
|
+
server_version: SERVER_VERSION,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
/* telemetry must never affect a request */
|
|
113
|
+
}
|
|
114
|
+
}
|
|
90
115
|
export async function shutdownTelemetry() {
|
|
91
116
|
const c = client;
|
|
92
117
|
client = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opedd-mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.14",
|
|
4
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",
|