@refkit/mcp 0.6.1 → 0.8.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.
- package/README.md +13 -6
- package/dist/{chunk-DQHNPSDF.js → chunk-SOL6EG4R.js} +52 -98
- package/dist/chunk-SOL6EG4R.js.map +1 -0
- package/dist/cli.d.ts +6 -1
- package/dist/cli.js +21 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +21 -20
- package/dist/chunk-DQHNPSDF.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Point any MCP client at:
|
|
|
10
10
|
npx -y @refkit/mcp
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
It boots with the keyless sources (Met, Art Institute of Chicago, Wikimedia Commons, Openverse + audio, Project Gutenberg, PoetryDB) and auto-adds any BYOK source whose key is in the environment. Each key is read as a unified `REFKIT_<PROVIDER>_KEY` name first, falling back to the provider's legacy name (both are honored indefinitely):
|
|
13
|
+
It boots with the keyless sources (Met, Art Institute of Chicago, Wikimedia Commons, Openverse + audio, Project Gutenberg, PoetryDB, Rijksmuseum, Poly Haven, ambientCG, Internet Archive, Nailbook — Japanese nail-art, discovery-class `license: 'unknown'`, best queried with Japanese tag words, and the one source that *declines* queries outside its domain) and auto-adds any BYOK source whose key is in the environment. Each key is read as a unified `REFKIT_<PROVIDER>_KEY` name first, falling back to the provider's legacy name (both are honored indefinitely):
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
REFKIT_UNSPLASH_KEY=… REFKIT_PEXELS_KEY=… REFKIT_PIXABAY_KEY=… REFKIT_FLICKR_KEY=… REFKIT_SMITHSONIAN_KEY=… REFKIT_BRAVE_KEY=… npx -y @refkit/mcp
|
|
@@ -19,6 +19,8 @@ REFKIT_UNSPLASH_KEY=… REFKIT_PEXELS_KEY=… REFKIT_PIXABAY_KEY=… REFKIT_FLIC
|
|
|
19
19
|
UNSPLASH_KEY=… PEXELS_KEY=… PIXABAY_KEY=… FLICKR_KEY=… SI_KEY=… BRAVE_TOKEN=… npx -y @refkit/mcp
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
If your MCP client clamps tool-output strings, `REFKIT_MAX_CURSOR_SEEN` shrinks the load-more cursor: it caps how many already-returned keys `nextCursor` remembers (default 500 ≈ 2.7k chars; `REFKIT_MAX_CURSOR_SEEN=200` ≈ 1.1k). A lower cap only risks re-showing results from that many batches ago on very deep pagination.
|
|
23
|
+
|
|
22
24
|
Example MCP client config:
|
|
23
25
|
|
|
24
26
|
```json
|
|
@@ -43,13 +45,18 @@ await serveStdio(createRefkit({
|
|
|
43
45
|
|
|
44
46
|
## The `search_references` tool
|
|
45
47
|
|
|
46
|
-
Input: `{ query, modalities?,
|
|
48
|
+
Input: `{ query, modalities?, sources?, controls?, providerOptions?, limit?, cursor?, rerank?, minRelevance?, deadlineMs?, intent?, gateFor?, gateContext?, explain? }`.
|
|
47
49
|
|
|
48
|
-
- `controls` — provider-neutral search controls such as `{ orientation, color, language, sort, safety, license, media }`; providers translate supported controls and report ignored controls when `explain: true`.
|
|
50
|
+
- `controls` — provider-neutral search controls such as `{ orientation, color, language, sort, safety, license, media, page }`; providers translate supported controls and report ignored controls when `explain: true`.
|
|
51
|
+
- `sources` — restrict the search to specific source ids (the ids are listed in the tool description under "Configured sources"). Naming a source also **bypasses** that source's own query-acceptance predicate, so a narrow source like Nailbook — which declines non-nail queries on an unscoped search and then appears as `skipped` with `reason: 'declined'` — still answers when asked for by name.
|
|
52
|
+
- `rerank` — **default true**: results are re-ranked for the query (term coverage incl. CJK over title/description/tags/excerpt, the fused cross-source relevance, resolution, source and near-duplicate diversity). Pass `false` for raw cross-source rank fusion order. `true` cannot re-enable reranking when the host built the client with `rerank: false`.
|
|
53
|
+
- `minRelevance` — drop results the ranker scored below this (0…1), reported as `meta.threshold { minRelevance, dropped }`. The bar is graded against the reranker's *blended* score: under the stock weights a result matching no query term still lands near `0.3` and a full match near `0.95`, so `0.5` is the practical "real matches only" line. It does **not** transfer to `rerank: false`, where max-normalised fusion puts the top result at exactly `1`.
|
|
54
|
+
- `deadlineMs` — bound the whole call (cursor page advances included). Sources still in flight are reported as `failed` and the rest are returned. It is a hard bound while the host leaves per-source resilience on (the default); with resilience disabled it binds only sources that honour the abort signal.
|
|
49
55
|
- `intent` — annotate each result with a **use-verdict** for that intended use (no filtering).
|
|
50
56
|
- `gateFor` — return only results whose license allows that intent.
|
|
51
|
-
- `
|
|
52
|
-
- `
|
|
57
|
+
- `gateContext` — `{ userJurisdiction }` for that gate, matching `evaluate_use`'s jurisdiction input: a source-declared jurisdiction that differs from the caller's defaults to `needs-review`, which `gateFor` then drops.
|
|
58
|
+
- `cursor` — the previous result's top-level `nextCursor` (always returned, no `explain` needed); fetches the next batch, deduped against earlier ones.
|
|
59
|
+
- `explain` — include provider status (incl. per-source `confidence`, `latencyMs`, skip `reason`), applied and ignored unified controls, warnings, and gate/threshold-drop metadata.
|
|
53
60
|
- `providerOptions` — typed provider-specific whitelisted controls keyed by provider id, for example:
|
|
54
61
|
|
|
55
62
|
```json
|
|
@@ -65,7 +72,7 @@ Input: `{ query, modalities?, controls?, filters?, providerOptions?, explain?, l
|
|
|
65
72
|
}
|
|
66
73
|
```
|
|
67
74
|
|
|
68
|
-
Output: `{ references: [{ id, title?, modality, provider, canonicalUrl, license, thumbnail?, excerpt?, useVerdict?, useExplanation?, attribution? }], meta? }`. When `intent` (or `gateFor`) is set, each result carries `useVerdict { decision, reason, confidence }`, a plain `useExplanation`, and — if the license requires it — a ready-to-use `attribution` credit line. When `explain: true`, `meta` includes per-provider `fulfilled` / `failed` / `skipped` status, applied/ignored control details, warnings, and gate/drop counts.
|
|
75
|
+
Output: `{ references: [{ id, title?, modality, kind?, provider, canonicalUrl, license, thumbnail?, excerpt?, useVerdict?, useExplanation?, attribution? }], nextCursor?, meta? }`. When `intent` (or `gateFor`) is set, each result carries `useVerdict { decision, reason, confidence }`, a plain `useExplanation`, and — if the license requires it — a ready-to-use `attribution` credit line. When `explain: true`, `meta` includes per-provider `fulfilled` / `failed` / `skipped` status, applied/ignored control details, warnings, and gate/threshold-drop counts. The `meta` shape comes from core's own `searchMetaSchema` — this server keeps no copy of the vocabulary.
|
|
69
76
|
|
|
70
77
|
> Results are references with a license id + source link — **not rights clearance, not legal advice**. `unknown` / `needs-review` results require the caller to verify the source's terms.
|
|
71
78
|
|
|
@@ -3,61 +3,8 @@ import { readFileSync } from "fs";
|
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { LICENSE_IDS, INTENTS, evaluateUse, buildAttribution, ccVersionFor,
|
|
7
|
-
var
|
|
8
|
-
var ORIENTATIONS = ["landscape", "portrait", "square"];
|
|
9
|
-
var SEARCH_CONTROL_KEYS = [
|
|
10
|
-
"orientation",
|
|
11
|
-
"color",
|
|
12
|
-
"language",
|
|
13
|
-
"sort",
|
|
14
|
-
"safety",
|
|
15
|
-
"license.commercial",
|
|
16
|
-
"license.modification",
|
|
17
|
-
"license.allowUnknown",
|
|
18
|
-
"media.kind",
|
|
19
|
-
"media.size",
|
|
20
|
-
"media.minWidth",
|
|
21
|
-
"media.minHeight",
|
|
22
|
-
"media.duration",
|
|
23
|
-
"creator.id",
|
|
24
|
-
"creator.name",
|
|
25
|
-
"text.copyright",
|
|
26
|
-
"page"
|
|
27
|
-
];
|
|
28
|
-
var filtersSchema = z.object({
|
|
29
|
-
color: z.string().optional(),
|
|
30
|
-
orientation: z.enum(ORIENTATIONS).optional(),
|
|
31
|
-
language: z.string().optional()
|
|
32
|
-
});
|
|
33
|
-
var searchControlKeySchema = z.enum(SEARCH_CONTROL_KEYS);
|
|
34
|
-
var searchControlsSchema = z.object({
|
|
35
|
-
orientation: z.enum(ORIENTATIONS).optional(),
|
|
36
|
-
color: z.string().optional(),
|
|
37
|
-
language: z.string().optional(),
|
|
38
|
-
sort: z.enum(["relevance", "latest", "popular", "interesting"]).optional(),
|
|
39
|
-
safety: z.enum(["strict", "moderate", "off"]).optional(),
|
|
40
|
-
license: z.object({
|
|
41
|
-
commercial: z.boolean().optional(),
|
|
42
|
-
modification: z.boolean().optional(),
|
|
43
|
-
allowUnknown: z.boolean().optional()
|
|
44
|
-
}).optional(),
|
|
45
|
-
media: z.object({
|
|
46
|
-
kind: z.enum(["photo", "illustration", "vector", "film", "animation"]).optional(),
|
|
47
|
-
size: z.enum(["small", "medium", "large"]).optional(),
|
|
48
|
-
minWidth: z.number().int().nonnegative().optional(),
|
|
49
|
-
minHeight: z.number().int().nonnegative().optional(),
|
|
50
|
-
duration: z.enum(["short", "medium", "long"]).optional()
|
|
51
|
-
}).optional(),
|
|
52
|
-
creator: z.object({
|
|
53
|
-
id: z.string().optional(),
|
|
54
|
-
name: z.string().optional()
|
|
55
|
-
}).optional(),
|
|
56
|
-
text: z.object({
|
|
57
|
-
copyright: z.enum(["public-domain", "copyrighted", "any"]).optional()
|
|
58
|
-
}).optional(),
|
|
59
|
-
page: z.number().int().positive().optional()
|
|
60
|
-
});
|
|
6
|
+
import { LICENSE_IDS, INTENTS, evaluateUse, buildAttribution, ccVersionFor, buildSearchControlsSchema, searchMetaSchema } from "@refkit/core";
|
|
7
|
+
var BASE_MEDIA_KINDS = ["photo", "illustration", "vector", "film", "animation"];
|
|
61
8
|
var providerOptionValueSchema = z.union([z.string(), z.number(), z.boolean(), z.array(z.string())]);
|
|
62
9
|
var providerOptionsSchema = z.record(z.string(), z.record(z.string(), providerOptionValueSchema));
|
|
63
10
|
var VERSION = (() => {
|
|
@@ -72,6 +19,7 @@ function toAgentRef(r, assessment) {
|
|
|
72
19
|
id: r.id,
|
|
73
20
|
title: r.title,
|
|
74
21
|
modality: r.modality,
|
|
22
|
+
kind: r.kind,
|
|
75
23
|
provider: r.source.providerId,
|
|
76
24
|
canonicalUrl: r.canonicalUrl,
|
|
77
25
|
license: r.rights.license,
|
|
@@ -93,6 +41,7 @@ var agentRefSchema = z.object({
|
|
|
93
41
|
id: z.string(),
|
|
94
42
|
title: z.string().optional(),
|
|
95
43
|
modality: z.string(),
|
|
44
|
+
kind: z.string().optional().describe("fine-grained resource kind, e.g. photo / texture / ebook"),
|
|
96
45
|
provider: z.string(),
|
|
97
46
|
canonicalUrl: z.string(),
|
|
98
47
|
license: z.string(),
|
|
@@ -102,56 +51,44 @@ var agentRefSchema = z.object({
|
|
|
102
51
|
useExplanation: z.string().optional().describe("plain-language use verdict summary for agents"),
|
|
103
52
|
attribution: z.string().optional().describe("ready-to-use credit line; present when the license requires attribution")
|
|
104
53
|
});
|
|
105
|
-
var searchMetaSchema = z.object({
|
|
106
|
-
query: z.string(),
|
|
107
|
-
modalities: z.array(z.enum(MODALITIES)),
|
|
108
|
-
limit: z.number(),
|
|
109
|
-
poolFactor: z.number(),
|
|
110
|
-
fetchLimit: z.number(),
|
|
111
|
-
appliedFilters: filtersSchema.optional(),
|
|
112
|
-
controls: z.object({
|
|
113
|
-
requested: z.array(searchControlKeySchema),
|
|
114
|
-
appliedByProvider: z.record(z.string(), z.array(searchControlKeySchema)),
|
|
115
|
-
ignoredByProvider: z.record(z.string(), z.array(searchControlKeySchema))
|
|
116
|
-
}).optional(),
|
|
117
|
-
providerOptions: z.array(z.string()).optional(),
|
|
118
|
-
providers: z.array(z.object({
|
|
119
|
-
providerId: z.string(),
|
|
120
|
-
status: z.enum(["fulfilled", "failed", "skipped"]),
|
|
121
|
-
returned: z.number().optional(),
|
|
122
|
-
accepted: z.number().optional(),
|
|
123
|
-
rejected: z.number().optional(),
|
|
124
|
-
reason: z.enum(["unsupported-modality"]).optional(),
|
|
125
|
-
error: z.string().optional(),
|
|
126
|
-
latencyMs: z.number().optional(),
|
|
127
|
-
cached: z.boolean().optional()
|
|
128
|
-
})),
|
|
129
|
-
gate: z.object({
|
|
130
|
-
intent: z.enum(INTENTS),
|
|
131
|
-
before: z.number(),
|
|
132
|
-
after: z.number(),
|
|
133
|
-
dropped: z.number()
|
|
134
|
-
}).optional(),
|
|
135
|
-
nextCursor: z.string().optional().describe("opaque load-more cursor; pass back as `cursor` to fetch the next page with cross-page dedup"),
|
|
136
|
-
warnings: z.array(z.string())
|
|
137
|
-
});
|
|
138
54
|
function createRefkitMcpServer(refkit) {
|
|
139
55
|
const server = new McpServer({ name: "refkit", version: VERSION });
|
|
56
|
+
const enabledSourceIds = refkit.providers.map((p) => p.id);
|
|
57
|
+
const registered = refkit.providers;
|
|
58
|
+
const modalityValues = [...new Set(registered.flatMap((p) => p.modalities))];
|
|
59
|
+
const kindValues = [.../* @__PURE__ */ new Set([...BASE_MEDIA_KINDS, ...registered.flatMap((p) => p.kinds ?? [])])];
|
|
60
|
+
const sourceList = registered.map((p) => {
|
|
61
|
+
const kinds = p.kinds?.length ? `\xB7${p.kinds.join(",")}` : "";
|
|
62
|
+
const desc = p.description ? `: ${p.description}` : "";
|
|
63
|
+
return `- ${p.id} (${p.modalities.join("/")}${kinds})${desc}`;
|
|
64
|
+
}).join("\n");
|
|
65
|
+
const searchControlsSchema = buildSearchControlsSchema(kindValues);
|
|
140
66
|
server.registerTool(
|
|
141
67
|
"search_references",
|
|
142
68
|
{
|
|
143
69
|
title: "Search creative references",
|
|
144
|
-
description: "Search license-normalized reference material (image / video / audio / text) across the configured sources. Every result carries a license id + canonical source link. Pass `intent` to annotate each result with a use-verdict (may I use this, is attribution required) WITHOUT filtering; pass `gateFor` to instead return only results whose license allows that intent. Results are references, not rights clearance \u2014 not legal advice
|
|
70
|
+
description: "Search license-normalized reference material (image / video / audio / text) across the configured sources. Every result carries a license id + canonical source link. Pass `intent` to annotate each result with a use-verdict (may I use this, is attribution required) WITHOUT filtering; pass `gateFor` to instead return only results whose license allows that intent. Results are references, not rights clearance \u2014 not legal advice.\n\nConfigured sources:\n" + sourceList,
|
|
145
71
|
inputSchema: {
|
|
146
72
|
query: z.string().describe('what to search for, e.g. "cyberpunk alley at night"'),
|
|
147
|
-
modalities: z.array(z.enum(
|
|
148
|
-
|
|
73
|
+
modalities: z.array(z.enum(modalityValues)).optional().describe('default ["image"]'),
|
|
74
|
+
sources: z.array(z.string()).optional().describe(
|
|
75
|
+
`restrict the search to specific sources by id (omit to search every configured source \u2014 see "Configured sources" in this tool description). Use to scope a search-engine operator (e.g. "site:example.com") to a web-discovery source without affecting other sources' queries.`
|
|
76
|
+
),
|
|
149
77
|
controls: searchControlsSchema.optional().describe("provider-neutral search controls; providers translate supported controls and report ignored controls in explain metadata"),
|
|
150
78
|
providerOptions: providerOptionsSchema.optional().describe("provider-specific search controls keyed by provider id; each provider whitelists supported keys"),
|
|
151
79
|
explain: z.boolean().optional().describe("include provider status, applied and ignored controls, warnings, gate/drop metadata, and the load-more cursor"),
|
|
152
80
|
limit: z.number().int().positive().optional(),
|
|
153
81
|
cursor: z.string().optional().describe("opaque cursor from a previous result's nextCursor \u2014 fetches the next batch, deduped against earlier batches"),
|
|
154
|
-
rerank: z.boolean().optional().describe("re-rank results by query relevance (term coverage incl. CJK, resolution, source diversity)
|
|
82
|
+
rerank: z.boolean().optional().describe("re-rank results by query relevance (term coverage incl. CJK over title/description/tags/excerpt, fused cross-source relevance, resolution, source and near-duplicate diversity). Default true \u2014 pass false for raw cross-source rank fusion. true cannot re-enable reranking when the host built the client with rerank: false"),
|
|
83
|
+
minRelevance: z.number().min(0).max(1).optional().describe(
|
|
84
|
+
"drop results the ranker scored below this, after reranking. Graded against the reranker's blended score, where a result matching no query term lands around 0.3 under the stock weights \u2014 so 0.5 keeps only real matches. Off by default (a bar can empty the batch). Does NOT transfer to rerank: false, where raw fusion is max-normalised and the top result is always 1"
|
|
85
|
+
),
|
|
86
|
+
deadlineMs: z.number().int().positive().optional().describe(
|
|
87
|
+
"whole-search deadline in ms, cursor page advances included (unlike the host's per-source timeout, which bounds one source search). Sources still in flight when it fires are reported as failed and the rest are returned; a hard bound while the host leaves per-source resilience on (the default), and otherwise only binding on sources that honour the abort signal"
|
|
88
|
+
),
|
|
89
|
+
gateContext: z.object({
|
|
90
|
+
userJurisdiction: z.string().optional().describe("caller's jurisdiction; a mismatch with a source-declared one defaults to needs-review")
|
|
91
|
+
}).optional().describe("context for the `gateFor` gate, matching the evaluate_use tool's jurisdiction inputs"),
|
|
155
92
|
intent: z.enum(INTENTS).optional().describe("annotate each result with a use-verdict for this intended use (no filtering)"),
|
|
156
93
|
gateFor: z.enum(INTENTS).optional().describe("only return results whose license allows this intended use")
|
|
157
94
|
},
|
|
@@ -161,19 +98,36 @@ function createRefkitMcpServer(refkit) {
|
|
|
161
98
|
meta: searchMetaSchema.optional()
|
|
162
99
|
}
|
|
163
100
|
},
|
|
164
|
-
async ({ query, modalities,
|
|
101
|
+
async ({ query, modalities, controls, providerOptions, explain, limit, cursor, rerank, minRelevance, deadlineMs, gateContext, intent, gateFor, sources }) => {
|
|
165
102
|
const searchInput = {
|
|
166
103
|
query,
|
|
167
104
|
modalities: modalities ?? ["image"],
|
|
168
|
-
|
|
105
|
+
sources,
|
|
169
106
|
controls,
|
|
170
107
|
providerOptions,
|
|
171
108
|
limit,
|
|
172
109
|
cursor,
|
|
173
|
-
|
|
174
|
-
|
|
110
|
+
// Core reranks by default; only an explicit false turns it off.
|
|
111
|
+
...rerank === false ? { rerank: false } : {},
|
|
112
|
+
minRelevance,
|
|
113
|
+
deadlineMs,
|
|
114
|
+
gateFor,
|
|
115
|
+
gateContext
|
|
175
116
|
};
|
|
176
|
-
|
|
117
|
+
let result;
|
|
118
|
+
try {
|
|
119
|
+
result = await refkit.searchWithMeta(searchInput);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
if (sources && sources.length > 0 && !(err instanceof AggregateError)) {
|
|
122
|
+
const available = enabledSourceIds.join(", ") || "(none)";
|
|
123
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
124
|
+
return {
|
|
125
|
+
isError: true,
|
|
126
|
+
content: [{ type: "text", text: `${detail} Enabled source ids: ${available}.` }]
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
177
131
|
const refs = result.references;
|
|
178
132
|
const assessIntent = intent ?? gateFor;
|
|
179
133
|
const references = refs.map(
|
|
@@ -274,4 +228,4 @@ export {
|
|
|
274
228
|
createRefkitMcpServer,
|
|
275
229
|
serveStdio
|
|
276
230
|
};
|
|
277
|
-
//# sourceMappingURL=chunk-
|
|
231
|
+
//# sourceMappingURL=chunk-SOL6EG4R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport { LICENSE_IDS, INTENTS, evaluateUse, buildAttribution, ccVersionFor, buildSearchControlsSchema, searchMetaSchema } from '@refkit/core'\nimport type { RefkitClient, Reference, Verdict, Attribution, SearchControls, SearchInput, ProviderOptionsById, RightsRecord, Modality } from '@refkit/core'\n\n// Legacy media.kind control values — kept in the dynamic enum because they stay\n// meaningful as upstream filter translations for providers that support the\n// media.kind control without declaring kinds.\nconst BASE_MEDIA_KINDS = ['photo', 'illustration', 'vector', 'film', 'animation'] as const\n\nconst providerOptionValueSchema = z.union([z.string(), z.number(), z.boolean(), z.array(z.string())])\nconst providerOptionsSchema = z.record(z.string(), z.record(z.string(), providerOptionValueSchema))\n\n// Reported in the MCP initialize handshake. Read the real version (the dist sits\n// next to package.json, which npm always ships) instead of a hardcoded placeholder.\nconst VERSION: string = (() => {\n try {\n return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version\n } catch {\n return '0.0.0'\n }\n})()\n\n// Concise, agent-facing projection of a Reference (no raw provider dump). When an\n// intent is supplied the use-gate verdict + attribution ride along, so the agent\n// sees *whether it may use* each result — not just a bare license id it ignores.\nfunction toAgentRef(r: Reference, assessment?: { verdict: Verdict; attribution: Attribution }) {\n const base = {\n id: r.id,\n title: r.title,\n modality: r.modality,\n kind: r.kind,\n provider: r.source.providerId,\n canonicalUrl: r.canonicalUrl,\n license: r.rights.license,\n thumbnail: r.thumbnail?.url,\n excerpt: r.text?.excerpt,\n }\n if (!assessment) return base\n const { verdict, attribution } = assessment\n const reason = verdict.reasons.join('; ')\n const useExplanation = `${verdict.decision}: ${reason || 'license facts allow this use'}${attribution.required && attribution.text ? ` Attribution required: ${attribution.text}` : ''}`\n return {\n ...base,\n useVerdict: { decision: verdict.decision, reason: verdict.reasons.join('; '), confidence: verdict.confidence },\n useExplanation,\n ...(attribution.required && attribution.text ? { attribution: attribution.text } : {}),\n }\n}\n\nconst agentRefSchema = z.object({\n id: z.string(),\n title: z.string().optional(),\n modality: z.string(),\n kind: z.string().optional().describe('fine-grained resource kind, e.g. photo / texture / ebook'),\n provider: z.string(),\n canonicalUrl: z.string(),\n license: z.string(),\n thumbnail: z.string().optional(),\n excerpt: z.string().optional(),\n useVerdict: z\n .object({ decision: z.string(), reason: z.string(), confidence: z.string() })\n .optional()\n .describe('present when `intent` (or `gateFor`) is set: may this be used for that intent, and how confident'),\n useExplanation: z.string().optional().describe('plain-language use verdict summary for agents'),\n attribution: z.string().optional().describe('ready-to-use credit line; present when the license requires attribution'),\n})\n\n/** Wrap a configured RefkitClient as an MCP server exposing `search_references`. */\nexport function createRefkitMcpServer(refkit: RefkitClient): McpServer {\n const server = new McpServer({ name: 'refkit', version: VERSION })\n // Enumerated in the `sources` param description so an agent knows the valid ids\n // up front (and reused to enrich a source-miss error). Providers are fixed for\n // the server's lifetime, so this snapshot never drifts.\n const enabledSourceIds = refkit.providers.map(p => p.id)\n\n const registered = refkit.providers\n const modalityValues = [...new Set(registered.flatMap(p => p.modalities))] as [Modality, ...Modality[]]\n const kindValues = [...new Set<string>([...BASE_MEDIA_KINDS, ...registered.flatMap(p => p.kinds ?? [])])] as [string, ...string[]]\n const sourceList = registered.map(p => {\n const kinds = p.kinds?.length ? `·${p.kinds.join(',')}` : ''\n const desc = p.description ? `: ${p.description}` : ''\n return `- ${p.id} (${p.modalities.join('/')}${kinds})${desc}`\n }).join('\\n')\n const searchControlsSchema = buildSearchControlsSchema(kindValues)\n\n server.registerTool(\n 'search_references',\n {\n title: 'Search creative references',\n description:\n 'Search license-normalized reference material (image / video / audio / text) across the configured sources. ' +\n 'Every result carries a license id + canonical source link. Pass `intent` to annotate each result with a ' +\n 'use-verdict (may I use this, is attribution required) WITHOUT filtering; pass `gateFor` to instead return ' +\n 'only results whose license allows that intent. Results are references, not rights clearance — not legal advice.' +\n '\\n\\nConfigured sources:\\n' + sourceList,\n inputSchema: {\n query: z.string().describe('what to search for, e.g. \"cyberpunk alley at night\"'),\n modalities: z.array(z.enum(modalityValues)).optional().describe('default [\"image\"]'),\n sources: z.array(z.string()).optional().describe(\n 'restrict the search to specific sources by id (omit to search every configured source — see \"Configured sources\" in this tool description). '\n + 'Use to scope a search-engine operator (e.g. \"site:example.com\") to a web-discovery source without affecting other sources\\' queries.',\n ),\n controls: searchControlsSchema.optional().describe('provider-neutral search controls; providers translate supported controls and report ignored controls in explain metadata'),\n providerOptions: providerOptionsSchema.optional().describe('provider-specific search controls keyed by provider id; each provider whitelists supported keys'),\n explain: z.boolean().optional().describe('include provider status, applied and ignored controls, warnings, gate/drop metadata, and the load-more cursor'),\n limit: z.number().int().positive().optional(),\n cursor: z.string().optional().describe('opaque cursor from a previous result\\'s nextCursor — fetches the next batch, deduped against earlier batches'),\n rerank: z.boolean().optional().describe('re-rank results by query relevance (term coverage incl. CJK over title/description/tags/excerpt, fused cross-source relevance, resolution, source and near-duplicate diversity). Default true — pass false for raw cross-source rank fusion. true cannot re-enable reranking when the host built the client with rerank: false'),\n minRelevance: z.number().min(0).max(1).optional().describe(\n 'drop results the ranker scored below this, after reranking. Graded against the reranker\\'s blended score, where a result matching no query term lands around 0.3 under the stock weights — so 0.5 keeps only real matches. Off by default (a bar can empty the batch). Does NOT transfer to rerank: false, where raw fusion is max-normalised and the top result is always 1',\n ),\n deadlineMs: z.number().int().positive().optional().describe(\n 'whole-search deadline in ms, cursor page advances included (unlike the host\\'s per-source timeout, which bounds one source search). Sources still in flight when it fires are reported as failed and the rest are returned; a hard bound while the host leaves per-source resilience on (the default), and otherwise only binding on sources that honour the abort signal',\n ),\n gateContext: z.object({\n userJurisdiction: z.string().optional().describe('caller\\'s jurisdiction; a mismatch with a source-declared one defaults to needs-review'),\n }).optional().describe('context for the `gateFor` gate, matching the evaluate_use tool\\'s jurisdiction inputs'),\n intent: z.enum(INTENTS).optional().describe('annotate each result with a use-verdict for this intended use (no filtering)'),\n gateFor: z.enum(INTENTS).optional().describe('only return results whose license allows this intended use'),\n },\n outputSchema: {\n references: z.array(agentRefSchema),\n nextCursor: z.string().optional().describe('pass back as `cursor` for the next batch; absent = exhausted'),\n meta: searchMetaSchema.optional(),\n },\n },\n async ({ query, modalities, controls, providerOptions, explain, limit, cursor, rerank, minRelevance, deadlineMs, gateContext, intent, gateFor, sources }) => {\n // Typed as core's SearchInput so every parameter above is checked against\n // the contract it is forwarded into, not just against zod.\n const searchInput: SearchInput = {\n query,\n modalities: modalities ?? ['image'],\n sources,\n controls: controls as SearchControls | undefined,\n providerOptions: providerOptions as ProviderOptionsById | undefined,\n limit,\n cursor,\n // Core reranks by default; only an explicit false turns it off.\n ...(rerank === false ? { rerank: false as const } : {}),\n minRelevance,\n deadlineMs,\n gateFor,\n gateContext,\n }\n // Always searchWithMeta: the continuation token (meta.nextCursor) must not\n // depend on the explain diagnostics flag — only the meta DUMP is gated.\n let result\n try {\n result = await refkit.searchWithMeta(searchInput)\n } catch (err) {\n // A source-selection miss (no requested id resolves for the modality) is a\n // caller mistake, not an outage — turn core's throw into an agent-actionable\n // tool error listing the valid ids. AggregateError (every chosen provider\n // failed at fetch) is a genuine upstream fault, so let it propagate.\n if (sources && sources.length > 0 && !(err instanceof AggregateError)) {\n const available = enabledSourceIds.join(', ') || '(none)'\n const detail = err instanceof Error ? err.message : String(err)\n return {\n isError: true,\n content: [{ type: 'text', text: `${detail} Enabled source ids: ${available}.` }],\n }\n }\n throw err\n }\n const refs = result.references\n const assessIntent = intent ?? gateFor\n const references = refs.map(r =>\n assessIntent\n ? toAgentRef(r, { verdict: refkit.evaluateUse(r, assessIntent), attribution: refkit.buildAttribution(r) })\n : toAgentRef(r),\n )\n return {\n content: [{ type: 'text', text: `${references.length} reference(s) for \"${query}\".` }],\n structuredContent: {\n references,\n ...(result.meta.nextCursor ? { nextCursor: result.meta.nextCursor } : {}),\n ...(explain ? { meta: result.meta } : {}),\n },\n }\n },\n )\n\n const attributionOutputSchema = { required: z.boolean(), text: z.string().optional(), html: z.string().optional() }\n\n server.registerTool(\n 'evaluate_use',\n {\n title: 'Evaluate a license for an intended use',\n description:\n 'Stateless license/use-gate check: given a license id + intended use, returns a conservative-heuristic verdict ' +\n '(allowed / allowed-with-attribution / denied / needs-review) with reasons and confidence. ' +\n 'Not legal advice — a strict-deny heuristic over source-declared license facts.',\n inputSchema: {\n license: z.enum(LICENSE_IDS).describe('the reference\\'s license id'),\n licenseVersion: z.string().optional().describe('precise CC version, e.g. \"4.0\" — attribution only'),\n author: z.string().optional(),\n title: z.string().optional(),\n canonicalUrl: z.string().describe('canonical source link, for attribution and audit'),\n intent: z.enum(INTENTS).describe('the intended use to evaluate this license against'),\n editorialOnly: z.boolean().optional().describe('source marked editorial-only'),\n jurisdiction: z.string().optional().describe('source-declared jurisdiction of the PD/copyright status'),\n userJurisdiction: z.string().optional().describe('caller\\'s jurisdiction; mismatched jurisdictions default to needs-review'),\n },\n outputSchema: {\n decision: z.enum(['allowed', 'allowed-with-attribution', 'denied', 'needs-review']),\n reasons: z.array(z.string()),\n confidence: z.enum(['high', 'low']),\n disclaimer: z.string(),\n attribution: z.object(attributionOutputSchema).optional(),\n },\n },\n async ({ license, licenseVersion, author, title, canonicalUrl, intent, editorialOnly, jurisdiction, userJurisdiction }) => {\n const version = ccVersionFor(license, licenseVersion)\n const rights: RightsRecord = {\n license,\n licenseVersion: version,\n author,\n rehostPolicy: 'cache-allowed',\n jurisdiction,\n editorialOnly,\n raw: { sourceTerms: '', sourceUrl: canonicalUrl },\n }\n const verdict: Verdict = evaluateUse(rights, intent, { userJurisdiction })\n const attribution =\n verdict.decision === 'allowed-with-attribution'\n ? buildAttribution({ license, licenseVersion: version, author, title, canonicalUrl })\n : undefined\n const summary = `${verdict.decision}: ${verdict.reasons.join('; ') || 'license facts allow this use'}`\n return {\n content: [{ type: 'text', text: summary }],\n structuredContent: {\n decision: verdict.decision,\n reasons: verdict.reasons,\n confidence: verdict.confidence,\n disclaimer: verdict.disclaimer,\n ...(attribution ? { attribution } : {}),\n },\n }\n },\n )\n\n server.registerTool(\n 'build_attribution',\n {\n title: 'Build an attribution credit line',\n description:\n 'Mechanically derive an attribution credit line (plain text + HTML) from a license id + author + title + ' +\n 'canonicalUrl. `required` is false when the license needs no attribution (e.g. CC0, PD).',\n inputSchema: {\n license: z.enum(LICENSE_IDS),\n licenseVersion: z.string().optional().describe('precise CC version, e.g. \"4.0\" — appended to the license name'),\n author: z.string().optional(),\n title: z.string().optional(),\n canonicalUrl: z.string(),\n },\n outputSchema: attributionOutputSchema,\n },\n async ({ license, licenseVersion, author, title, canonicalUrl }) => {\n const version = ccVersionFor(license, licenseVersion)\n const attribution: Attribution = buildAttribution({ license, licenseVersion: version, author, title, canonicalUrl })\n return {\n content: [{ type: 'text', text: attribution.required ? (attribution.text ?? '') : 'No attribution required for this license.' }],\n structuredContent: { required: attribution.required, text: attribution.text, html: attribution.html },\n }\n },\n )\n\n return server\n}\n\n/** Run the refkit MCP server over stdio (host wires the RefkitClient + its providers/keys). */\nexport async function serveStdio(refkit: RefkitClient): Promise<void> {\n const server = createRefkitMcpServer(refkit)\n await server.connect(new StdioServerTransport())\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.d.ts
CHANGED
|
@@ -16,5 +16,10 @@ declare const BYOK_SOURCES: ByokSource[];
|
|
|
16
16
|
* (optionalDependencies, dynamically imported on demand). Exported so the
|
|
17
17
|
* wiring is unit-testable. */
|
|
18
18
|
declare function defaultProviders(env?: NodeJS.ProcessEnv): Promise<ReferenceProvider[]>;
|
|
19
|
+
/** Cursor size knob for size-clamped tool-output channels: REFKIT_MAX_CURSOR_SEEN
|
|
20
|
+
* caps how many already-returned keys the load-more cursor remembers (see
|
|
21
|
+
* RefkitOptions.maxCursorSeen in @refkit/core — default 500 ≈ 2.7k chars of
|
|
22
|
+
* nextCursor). Invalid values warn on stderr and fall back to that default. */
|
|
23
|
+
declare function maxCursorSeenFromEnv(env?: NodeJS.ProcessEnv): number | undefined;
|
|
19
24
|
|
|
20
|
-
export { BYOK_SOURCES, defaultProviders };
|
|
25
|
+
export { BYOK_SOURCES, defaultProviders, maxCursorSeenFromEnv };
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
serveStdio
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SOL6EG4R.js";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
import { realpathSync } from "fs";
|
|
@@ -16,6 +16,7 @@ import { poetrydb } from "@refkit/provider-poetrydb";
|
|
|
16
16
|
import { rijksmuseum } from "@refkit/provider-rijksmuseum";
|
|
17
17
|
import { polyhaven, ambientcg } from "@refkit/provider-polyhaven";
|
|
18
18
|
import { internetArchive } from "@refkit/provider-internet-archive";
|
|
19
|
+
import { nailbook } from "@refkit/provider-nailbook";
|
|
19
20
|
var BYOK_SOURCES = [
|
|
20
21
|
{
|
|
21
22
|
pkg: "@refkit/provider-unsplash",
|
|
@@ -81,7 +82,8 @@ async function defaultProviders(env = process.env) {
|
|
|
81
82
|
rijksmuseum(),
|
|
82
83
|
polyhaven(),
|
|
83
84
|
ambientcg(),
|
|
84
|
-
internetArchive()
|
|
85
|
+
internetArchive(),
|
|
86
|
+
nailbook()
|
|
85
87
|
];
|
|
86
88
|
const loaded = await Promise.all(BYOK_SOURCES.map(async (source) => {
|
|
87
89
|
const key = source.key(env);
|
|
@@ -109,11 +111,26 @@ var isEntry = (() => {
|
|
|
109
111
|
return false;
|
|
110
112
|
}
|
|
111
113
|
})();
|
|
114
|
+
function maxCursorSeenFromEnv(env = process.env) {
|
|
115
|
+
const raw = env.REFKIT_MAX_CURSOR_SEEN;
|
|
116
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
117
|
+
const n = /^\d+$/.test(raw) ? Number(raw) : Number.NaN;
|
|
118
|
+
if (!Number.isSafeInteger(n) || n < 1) {
|
|
119
|
+
console.error(`[refkit-mcp] ignoring invalid REFKIT_MAX_CURSOR_SEEN=${JSON.stringify(raw)} \u2014 expected a positive integer.`);
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
return n;
|
|
123
|
+
}
|
|
112
124
|
if (isEntry) {
|
|
113
|
-
|
|
125
|
+
const maxCursorSeen = maxCursorSeenFromEnv();
|
|
126
|
+
await serveStdio(createRefkit({
|
|
127
|
+
providers: await defaultProviders(),
|
|
128
|
+
...maxCursorSeen !== void 0 ? { maxCursorSeen } : {}
|
|
129
|
+
}));
|
|
114
130
|
}
|
|
115
131
|
export {
|
|
116
132
|
BYOK_SOURCES,
|
|
117
|
-
defaultProviders
|
|
133
|
+
defaultProviders,
|
|
134
|
+
maxCursorSeenFromEnv
|
|
118
135
|
};
|
|
119
136
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n// Zero-config entry: `npx @refkit/mcp` boots a working MCP server with the keyless\n// providers, plus any BYOK provider whose key is in the environment. No host code.\nimport { realpathSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { createRefkit, type ReferenceProvider } from '@refkit/core'\nimport { openverse, openverseAudio } from '@refkit/provider-openverse'\nimport { wikimediaCommons } from '@refkit/provider-wikimedia-commons'\nimport { met } from '@refkit/provider-met'\nimport { artic } from '@refkit/provider-artic'\nimport { gutendex } from '@refkit/provider-gutendex'\nimport { poetrydb } from '@refkit/provider-poetrydb'\nimport { rijksmuseum } from '@refkit/provider-rijksmuseum'\nimport { polyhaven, ambientcg } from '@refkit/provider-polyhaven'\nimport { internetArchive } from '@refkit/provider-internet-archive'\nimport { serveStdio } from './index'\n\n/** One BYOK source: its unified + legacy env var names, and a lazy loader for its\n * (optional) provider package. Loaded only when a key is present, so installs\n * that omitted optionalDependencies still boot — the missing source is skipped\n * with a stderr warning instead of crashing the server. */\ninterface ByokSource {\n pkg: string\n key: (env: NodeJS.ProcessEnv) => string | undefined\n load: (key: string) => Promise<ReferenceProvider[]>\n}\n\n// Env var convention: each BYOK key is read as `REFKIT_<PROVIDER>_KEY` first (the\n// unified name), falling back to the provider's legacy env var name — both are\n// honored indefinitely, the unified name is just preferred going forward.\n// Exported so tests can assert this table stays in sync with the package's\n// optionalDependencies — an entry in one but not the other ships a source that\n// either can never load or never installs.\nexport const BYOK_SOURCES: ByokSource[] = [\n {\n pkg: '@refkit/provider-unsplash',\n key: (env) => env.REFKIT_UNSPLASH_KEY ?? env.UNSPLASH_KEY,\n load: async (accessKey) => [(await import('@refkit/provider-unsplash')).unsplash({ accessKey })],\n },\n {\n pkg: '@refkit/provider-pexels',\n key: (env) => env.REFKIT_PEXELS_KEY ?? env.PEXELS_KEY,\n load: async (apiKey) => {\n const m = await import('@refkit/provider-pexels')\n return [m.pexels({ apiKey }), m.pexelsVideo({ apiKey })]\n },\n },\n {\n pkg: '@refkit/provider-pixabay',\n key: (env) => env.REFKIT_PIXABAY_KEY ?? env.PIXABAY_KEY,\n load: async (key) => {\n const m = await import('@refkit/provider-pixabay')\n return [m.pixabay({ key }), m.pixabayVideo({ key })]\n },\n },\n {\n pkg: '@refkit/provider-flickr',\n key: (env) => env.REFKIT_FLICKR_KEY ?? env.FLICKR_KEY,\n load: async (apiKey) => [(await import('@refkit/provider-flickr')).flickr({ apiKey })],\n },\n {\n pkg: '@refkit/provider-smithsonian',\n key: (env) => env.REFKIT_SMITHSONIAN_KEY ?? env.SI_KEY,\n load: async (apiKey) => [(await import('@refkit/provider-smithsonian')).smithsonian({ apiKey })],\n },\n {\n pkg: '@refkit/provider-brave',\n key: (env) => env.REFKIT_BRAVE_KEY ?? env.BRAVE_TOKEN,\n load: async (token) => [(await import('@refkit/provider-brave')).brave({ token })],\n },\n {\n pkg: '@refkit/provider-freesound',\n key: (env) => env.REFKIT_FREESOUND_KEY ?? env.FREESOUND_TOKEN,\n load: async (apiKey) => [(await import('@refkit/provider-freesound')).freesound({ apiKey })],\n },\n {\n pkg: '@refkit/provider-jamendo',\n key: (env) => env.REFKIT_JAMENDO_CLIENT_ID ?? env.JAMENDO_CLIENT_ID,\n load: async (clientId) => [(await import('@refkit/provider-jamendo')).jamendo({ clientId })],\n },\n {\n pkg: '@refkit/provider-europeana',\n key: (env) => env.REFKIT_EUROPEANA_KEY ?? env.EUROPEANA_KEY,\n load: async (apiKey) => [(await import('@refkit/provider-europeana')).europeana({ apiKey })],\n },\n]\n\n/** Providers a zero-config server boots with: all keyless sources (hard deps,\n * statically imported), plus any BYOK source whose key is present in `env`\n * (optionalDependencies, dynamically imported on demand). Exported so the\n * wiring is unit-testable. */\nexport async function defaultProviders(env: NodeJS.ProcessEnv = process.env): Promise<ReferenceProvider[]> {\n const providers: ReferenceProvider[] = [\n openverse(), openverseAudio(), wikimediaCommons(), met(), artic(), gutendex(), poetrydb(),\n rijksmuseum(), polyhaven(), ambientcg(), internetArchive(),\n ]\n // Independent module loads — run them concurrently (startup cost = max, not sum)\n // and keep BYOK_SOURCES order in the provider list.\n const loaded = await Promise.all(BYOK_SOURCES.map(async (source) => {\n const key = source.key(env)\n if (!key) return []\n try {\n return await source.load(key)\n } catch (err) {\n // stderr only — stdout is the MCP transport. Only a genuinely missing\n // module gets the \"not installed\" hint; anything else (factory throw,\n // broken transitive import) surfaces the REAL error so the operator\n // doesn't chase the wrong remediation.\n const code = (err as { code?: string } | null)?.code\n if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') {\n console.error(`[refkit-mcp] key for ${source.pkg} is set but the package is not installed — skipping this source. Reinstall @refkit/mcp with optional dependencies (or add ${source.pkg}) to enable it.`)\n } else {\n console.error(`[refkit-mcp] failed to initialize ${source.pkg} — skipping this source.`, err)\n }\n return []\n }\n }))\n for (const group of loaded) providers.push(...group)\n return providers\n}\n\n// Boot only when run as the CLI entry — not when imported (e.g. by tests). realpath\n// so an npm bin symlink resolves to this file.\nconst isEntry = (() => {\n if (!process.argv[1]) return false\n try {\n return realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)\n } catch {\n return false\n }\n})()\n\nif (isEntry) {\n await serveStdio(createRefkit({ providers: await defaultProviders() }))\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n// Zero-config entry: `npx @refkit/mcp` boots a working MCP server with the keyless\n// providers, plus any BYOK provider whose key is in the environment. No host code.\nimport { realpathSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { createRefkit, type ReferenceProvider } from '@refkit/core'\nimport { openverse, openverseAudio } from '@refkit/provider-openverse'\nimport { wikimediaCommons } from '@refkit/provider-wikimedia-commons'\nimport { met } from '@refkit/provider-met'\nimport { artic } from '@refkit/provider-artic'\nimport { gutendex } from '@refkit/provider-gutendex'\nimport { poetrydb } from '@refkit/provider-poetrydb'\nimport { rijksmuseum } from '@refkit/provider-rijksmuseum'\nimport { polyhaven, ambientcg } from '@refkit/provider-polyhaven'\nimport { internetArchive } from '@refkit/provider-internet-archive'\nimport { nailbook } from '@refkit/provider-nailbook'\nimport { serveStdio } from './index'\n\n/** One BYOK source: its unified + legacy env var names, and a lazy loader for its\n * (optional) provider package. Loaded only when a key is present, so installs\n * that omitted optionalDependencies still boot — the missing source is skipped\n * with a stderr warning instead of crashing the server. */\ninterface ByokSource {\n pkg: string\n key: (env: NodeJS.ProcessEnv) => string | undefined\n load: (key: string) => Promise<ReferenceProvider[]>\n}\n\n// Env var convention: each BYOK key is read as `REFKIT_<PROVIDER>_KEY` first (the\n// unified name), falling back to the provider's legacy env var name — both are\n// honored indefinitely, the unified name is just preferred going forward.\n// Exported so tests can assert this table stays in sync with the package's\n// optionalDependencies — an entry in one but not the other ships a source that\n// either can never load or never installs.\nexport const BYOK_SOURCES: ByokSource[] = [\n {\n pkg: '@refkit/provider-unsplash',\n key: (env) => env.REFKIT_UNSPLASH_KEY ?? env.UNSPLASH_KEY,\n load: async (accessKey) => [(await import('@refkit/provider-unsplash')).unsplash({ accessKey })],\n },\n {\n pkg: '@refkit/provider-pexels',\n key: (env) => env.REFKIT_PEXELS_KEY ?? env.PEXELS_KEY,\n load: async (apiKey) => {\n const m = await import('@refkit/provider-pexels')\n return [m.pexels({ apiKey }), m.pexelsVideo({ apiKey })]\n },\n },\n {\n pkg: '@refkit/provider-pixabay',\n key: (env) => env.REFKIT_PIXABAY_KEY ?? env.PIXABAY_KEY,\n load: async (key) => {\n const m = await import('@refkit/provider-pixabay')\n return [m.pixabay({ key }), m.pixabayVideo({ key })]\n },\n },\n {\n pkg: '@refkit/provider-flickr',\n key: (env) => env.REFKIT_FLICKR_KEY ?? env.FLICKR_KEY,\n load: async (apiKey) => [(await import('@refkit/provider-flickr')).flickr({ apiKey })],\n },\n {\n pkg: '@refkit/provider-smithsonian',\n key: (env) => env.REFKIT_SMITHSONIAN_KEY ?? env.SI_KEY,\n load: async (apiKey) => [(await import('@refkit/provider-smithsonian')).smithsonian({ apiKey })],\n },\n {\n pkg: '@refkit/provider-brave',\n key: (env) => env.REFKIT_BRAVE_KEY ?? env.BRAVE_TOKEN,\n load: async (token) => [(await import('@refkit/provider-brave')).brave({ token })],\n },\n {\n pkg: '@refkit/provider-freesound',\n key: (env) => env.REFKIT_FREESOUND_KEY ?? env.FREESOUND_TOKEN,\n load: async (apiKey) => [(await import('@refkit/provider-freesound')).freesound({ apiKey })],\n },\n {\n pkg: '@refkit/provider-jamendo',\n key: (env) => env.REFKIT_JAMENDO_CLIENT_ID ?? env.JAMENDO_CLIENT_ID,\n load: async (clientId) => [(await import('@refkit/provider-jamendo')).jamendo({ clientId })],\n },\n {\n pkg: '@refkit/provider-europeana',\n key: (env) => env.REFKIT_EUROPEANA_KEY ?? env.EUROPEANA_KEY,\n load: async (apiKey) => [(await import('@refkit/provider-europeana')).europeana({ apiKey })],\n },\n]\n\n/** Providers a zero-config server boots with: all keyless sources (hard deps,\n * statically imported), plus any BYOK source whose key is present in `env`\n * (optionalDependencies, dynamically imported on demand). Exported so the\n * wiring is unit-testable. */\nexport async function defaultProviders(env: NodeJS.ProcessEnv = process.env): Promise<ReferenceProvider[]> {\n const providers: ReferenceProvider[] = [\n openverse(), openverseAudio(), wikimediaCommons(), met(), artic(), gutendex(), poetrydb(),\n rijksmuseum(), polyhaven(), ambientcg(), internetArchive(), nailbook(),\n ]\n // Independent module loads — run them concurrently (startup cost = max, not sum)\n // and keep BYOK_SOURCES order in the provider list.\n const loaded = await Promise.all(BYOK_SOURCES.map(async (source) => {\n const key = source.key(env)\n if (!key) return []\n try {\n return await source.load(key)\n } catch (err) {\n // stderr only — stdout is the MCP transport. Only a genuinely missing\n // module gets the \"not installed\" hint; anything else (factory throw,\n // broken transitive import) surfaces the REAL error so the operator\n // doesn't chase the wrong remediation.\n const code = (err as { code?: string } | null)?.code\n if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') {\n console.error(`[refkit-mcp] key for ${source.pkg} is set but the package is not installed — skipping this source. Reinstall @refkit/mcp with optional dependencies (or add ${source.pkg}) to enable it.`)\n } else {\n console.error(`[refkit-mcp] failed to initialize ${source.pkg} — skipping this source.`, err)\n }\n return []\n }\n }))\n for (const group of loaded) providers.push(...group)\n return providers\n}\n\n// Boot only when run as the CLI entry — not when imported (e.g. by tests). realpath\n// so an npm bin symlink resolves to this file.\nconst isEntry = (() => {\n if (!process.argv[1]) return false\n try {\n return realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)\n } catch {\n return false\n }\n})()\n\n/** Cursor size knob for size-clamped tool-output channels: REFKIT_MAX_CURSOR_SEEN\n * caps how many already-returned keys the load-more cursor remembers (see\n * RefkitOptions.maxCursorSeen in @refkit/core — default 500 ≈ 2.7k chars of\n * nextCursor). Invalid values warn on stderr and fall back to that default. */\nexport function maxCursorSeenFromEnv(env: NodeJS.ProcessEnv = process.env): number | undefined {\n const raw = env.REFKIT_MAX_CURSOR_SEEN\n if (raw === undefined || raw === '') return undefined\n // Strict decimal digits only — Number()'s hex/exponent/whitespace leniency\n // would silently accept values like '1e100' that defeat the cap entirely.\n const n = /^\\d+$/.test(raw) ? Number(raw) : Number.NaN\n if (!Number.isSafeInteger(n) || n < 1) {\n console.error(`[refkit-mcp] ignoring invalid REFKIT_MAX_CURSOR_SEEN=${JSON.stringify(raw)} — expected a positive integer.`)\n return undefined\n }\n return n\n}\n\nif (isEntry) {\n const maxCursorSeen = maxCursorSeenFromEnv()\n await serveStdio(createRefkit({\n providers: await defaultProviders(),\n ...(maxCursorSeen !== undefined ? { maxCursorSeen } : {}),\n }))\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@refkit/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "MCP server for refkit — exposes license-normalized reference search as an agent tool.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -34,31 +34,32 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
36
36
|
"zod": "^4.3.6",
|
|
37
|
-
"@refkit/core": "0.
|
|
38
|
-
"@refkit/provider-
|
|
39
|
-
"@refkit/provider-
|
|
40
|
-
"@refkit/provider-
|
|
41
|
-
"@refkit/provider-
|
|
42
|
-
"@refkit/provider-openverse": "0.
|
|
43
|
-
"@refkit/provider-
|
|
44
|
-
"@refkit/provider-
|
|
45
|
-
"@refkit/provider-
|
|
46
|
-
"@refkit/provider-
|
|
37
|
+
"@refkit/core": "0.9.0",
|
|
38
|
+
"@refkit/provider-gutendex": "0.4.0",
|
|
39
|
+
"@refkit/provider-artic": "0.4.0",
|
|
40
|
+
"@refkit/provider-met": "0.4.0",
|
|
41
|
+
"@refkit/provider-internet-archive": "0.5.0",
|
|
42
|
+
"@refkit/provider-openverse": "0.5.0",
|
|
43
|
+
"@refkit/provider-wikimedia-commons": "0.5.0",
|
|
44
|
+
"@refkit/provider-poetrydb": "0.4.0",
|
|
45
|
+
"@refkit/provider-polyhaven": "0.4.0",
|
|
46
|
+
"@refkit/provider-rijksmuseum": "0.4.0",
|
|
47
|
+
"@refkit/provider-nailbook": "0.2.0"
|
|
47
48
|
},
|
|
48
49
|
"files": [
|
|
49
50
|
"dist",
|
|
50
51
|
"LICENSE"
|
|
51
52
|
],
|
|
52
53
|
"optionalDependencies": {
|
|
53
|
-
"@refkit/provider-brave": "0.
|
|
54
|
-
"@refkit/provider-europeana": "0.
|
|
55
|
-
"@refkit/provider-flickr": "0.
|
|
56
|
-
"@refkit/provider-
|
|
57
|
-
"@refkit/provider-
|
|
58
|
-
"@refkit/provider-
|
|
59
|
-
"@refkit/provider-
|
|
60
|
-
"@refkit/provider-
|
|
61
|
-
"@refkit/provider-
|
|
54
|
+
"@refkit/provider-brave": "0.4.0",
|
|
55
|
+
"@refkit/provider-europeana": "0.5.0",
|
|
56
|
+
"@refkit/provider-flickr": "0.5.0",
|
|
57
|
+
"@refkit/provider-freesound": "0.5.0",
|
|
58
|
+
"@refkit/provider-jamendo": "0.5.0",
|
|
59
|
+
"@refkit/provider-pexels": "0.4.0",
|
|
60
|
+
"@refkit/provider-pixabay": "0.4.0",
|
|
61
|
+
"@refkit/provider-smithsonian": "0.4.0",
|
|
62
|
+
"@refkit/provider-unsplash": "0.4.0"
|
|
62
63
|
},
|
|
63
64
|
"scripts": {
|
|
64
65
|
"typecheck": "tsc --noEmit",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport { LICENSE_IDS, INTENTS, evaluateUse, buildAttribution, ccVersionFor, lexicalReranker } from '@refkit/core'\nimport type { RefkitClient, Reference, Verdict, Attribution, SearchFilters, SearchControls, SearchControlKey, ProviderOptionsById, SearchMeta, RightsRecord } from '@refkit/core'\n\nconst MODALITIES = ['image', 'video', 'audio', 'text'] as const\nconst ORIENTATIONS = ['landscape', 'portrait', 'square'] as const\nconst SEARCH_CONTROL_KEYS = [\n 'orientation',\n 'color',\n 'language',\n 'sort',\n 'safety',\n 'license.commercial',\n 'license.modification',\n 'license.allowUnknown',\n 'media.kind',\n 'media.size',\n 'media.minWidth',\n 'media.minHeight',\n 'media.duration',\n 'creator.id',\n 'creator.name',\n 'text.copyright',\n 'page',\n] as const satisfies readonly SearchControlKey[]\n\nconst filtersSchema = z.object({\n color: z.string().optional(),\n orientation: z.enum(ORIENTATIONS).optional(),\n language: z.string().optional(),\n})\nconst searchControlKeySchema = z.enum(SEARCH_CONTROL_KEYS)\n\nconst searchControlsSchema = z.object({\n orientation: z.enum(ORIENTATIONS).optional(),\n color: z.string().optional(),\n language: z.string().optional(),\n sort: z.enum(['relevance', 'latest', 'popular', 'interesting']).optional(),\n safety: z.enum(['strict', 'moderate', 'off']).optional(),\n license: z.object({\n commercial: z.boolean().optional(),\n modification: z.boolean().optional(),\n allowUnknown: z.boolean().optional(),\n }).optional(),\n media: z.object({\n kind: z.enum(['photo', 'illustration', 'vector', 'film', 'animation']).optional(),\n size: z.enum(['small', 'medium', 'large']).optional(),\n minWidth: z.number().int().nonnegative().optional(),\n minHeight: z.number().int().nonnegative().optional(),\n duration: z.enum(['short', 'medium', 'long']).optional(),\n }).optional(),\n creator: z.object({\n id: z.string().optional(),\n name: z.string().optional(),\n }).optional(),\n text: z.object({\n copyright: z.enum(['public-domain', 'copyrighted', 'any']).optional(),\n }).optional(),\n page: z.number().int().positive().optional(),\n})\n\nconst providerOptionValueSchema = z.union([z.string(), z.number(), z.boolean(), z.array(z.string())])\nconst providerOptionsSchema = z.record(z.string(), z.record(z.string(), providerOptionValueSchema))\n\n// Reported in the MCP initialize handshake. Read the real version (the dist sits\n// next to package.json, which npm always ships) instead of a hardcoded placeholder.\nconst VERSION: string = (() => {\n try {\n return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version\n } catch {\n return '0.0.0'\n }\n})()\n\n// Concise, agent-facing projection of a Reference (no raw provider dump). When an\n// intent is supplied the use-gate verdict + attribution ride along, so the agent\n// sees *whether it may use* each result — not just a bare license id it ignores.\nfunction toAgentRef(r: Reference, assessment?: { verdict: Verdict; attribution: Attribution }) {\n const base = {\n id: r.id,\n title: r.title,\n modality: r.modality,\n provider: r.source.providerId,\n canonicalUrl: r.canonicalUrl,\n license: r.rights.license,\n thumbnail: r.thumbnail?.url,\n excerpt: r.text?.excerpt,\n }\n if (!assessment) return base\n const { verdict, attribution } = assessment\n const reason = verdict.reasons.join('; ')\n const useExplanation = `${verdict.decision}: ${reason || 'license facts allow this use'}${attribution.required && attribution.text ? ` Attribution required: ${attribution.text}` : ''}`\n return {\n ...base,\n useVerdict: { decision: verdict.decision, reason: verdict.reasons.join('; '), confidence: verdict.confidence },\n useExplanation,\n ...(attribution.required && attribution.text ? { attribution: attribution.text } : {}),\n }\n}\n\nconst agentRefSchema = z.object({\n id: z.string(),\n title: z.string().optional(),\n modality: z.string(),\n provider: z.string(),\n canonicalUrl: z.string(),\n license: z.string(),\n thumbnail: z.string().optional(),\n excerpt: z.string().optional(),\n useVerdict: z\n .object({ decision: z.string(), reason: z.string(), confidence: z.string() })\n .optional()\n .describe('present when `intent` (or `gateFor`) is set: may this be used for that intent, and how confident'),\n useExplanation: z.string().optional().describe('plain-language use verdict summary for agents'),\n attribution: z.string().optional().describe('ready-to-use credit line; present when the license requires attribution'),\n})\n\nconst searchMetaSchema: z.ZodType<SearchMeta> = z.object({\n query: z.string(),\n modalities: z.array(z.enum(MODALITIES)),\n limit: z.number(),\n poolFactor: z.number(),\n fetchLimit: z.number(),\n appliedFilters: filtersSchema.optional(),\n controls: z.object({\n requested: z.array(searchControlKeySchema),\n appliedByProvider: z.record(z.string(), z.array(searchControlKeySchema)),\n ignoredByProvider: z.record(z.string(), z.array(searchControlKeySchema)),\n }).optional(),\n providerOptions: z.array(z.string()).optional(),\n providers: z.array(z.object({\n providerId: z.string(),\n status: z.enum(['fulfilled', 'failed', 'skipped']),\n returned: z.number().optional(),\n accepted: z.number().optional(),\n rejected: z.number().optional(),\n reason: z.enum(['unsupported-modality']).optional(),\n error: z.string().optional(),\n latencyMs: z.number().optional(),\n cached: z.boolean().optional(),\n })),\n gate: z.object({\n intent: z.enum(INTENTS),\n before: z.number(),\n after: z.number(),\n dropped: z.number(),\n }).optional(),\n nextCursor: z.string().optional().describe('opaque load-more cursor; pass back as `cursor` to fetch the next page with cross-page dedup'),\n warnings: z.array(z.string()),\n})\n\n/** Wrap a configured RefkitClient as an MCP server exposing `search_references`. */\nexport function createRefkitMcpServer(refkit: RefkitClient): McpServer {\n const server = new McpServer({ name: 'refkit', version: VERSION })\n\n server.registerTool(\n 'search_references',\n {\n title: 'Search creative references',\n description:\n 'Search license-normalized reference material (image / video / audio / text) across the configured sources. ' +\n 'Every result carries a license id + canonical source link. Pass `intent` to annotate each result with a ' +\n 'use-verdict (may I use this, is attribution required) WITHOUT filtering; pass `gateFor` to instead return ' +\n 'only results whose license allows that intent. Results are references, not rights clearance — not legal advice.',\n inputSchema: {\n query: z.string().describe('what to search for, e.g. \"cyberpunk alley at night\"'),\n modalities: z.array(z.enum(MODALITIES)).optional().describe('default [\"image\"]'),\n filters: filtersSchema.optional().describe('compatibility alias for controls.orientation, controls.color, and controls.language'),\n controls: searchControlsSchema.optional().describe('provider-neutral search controls; providers translate supported controls and report ignored controls in explain metadata'),\n providerOptions: providerOptionsSchema.optional().describe('provider-specific search controls keyed by provider id; each provider whitelists supported keys'),\n explain: z.boolean().optional().describe('include provider status, applied and ignored controls, warnings, gate/drop metadata, and the load-more cursor'),\n limit: z.number().int().positive().optional(),\n cursor: z.string().optional().describe('opaque cursor from a previous result\\'s nextCursor — fetches the next batch, deduped against earlier batches'),\n rerank: z.boolean().optional().describe('re-rank results by query relevance (term coverage incl. CJK, resolution, source diversity) instead of raw cross-source rank fusion'),\n intent: z.enum(INTENTS).optional().describe('annotate each result with a use-verdict for this intended use (no filtering)'),\n gateFor: z.enum(INTENTS).optional().describe('only return results whose license allows this intended use'),\n },\n outputSchema: {\n references: z.array(agentRefSchema),\n nextCursor: z.string().optional().describe('pass back as `cursor` for the next batch; absent = exhausted'),\n meta: searchMetaSchema.optional(),\n },\n },\n async ({ query, modalities, filters, controls, providerOptions, explain, limit, cursor, rerank, intent, gateFor }) => {\n const searchInput = {\n query,\n modalities: modalities ?? ['image'],\n filters: filters as SearchFilters | undefined,\n controls: controls as SearchControls | undefined,\n providerOptions: providerOptions as ProviderOptionsById | undefined,\n limit,\n cursor,\n ...(rerank ? { rerank: lexicalReranker() } : {}),\n gateFor,\n }\n // Always searchWithMeta: the continuation token (meta.nextCursor) must not\n // depend on the explain diagnostics flag — only the meta DUMP is gated.\n const result = await refkit.searchWithMeta(searchInput)\n const refs = result.references\n const assessIntent = intent ?? gateFor\n const references = refs.map(r =>\n assessIntent\n ? toAgentRef(r, { verdict: refkit.evaluateUse(r, assessIntent), attribution: refkit.buildAttribution(r) })\n : toAgentRef(r),\n )\n return {\n content: [{ type: 'text', text: `${references.length} reference(s) for \"${query}\".` }],\n structuredContent: {\n references,\n ...(result.meta.nextCursor ? { nextCursor: result.meta.nextCursor } : {}),\n ...(explain ? { meta: result.meta } : {}),\n },\n }\n },\n )\n\n const attributionOutputSchema = { required: z.boolean(), text: z.string().optional(), html: z.string().optional() }\n\n server.registerTool(\n 'evaluate_use',\n {\n title: 'Evaluate a license for an intended use',\n description:\n 'Stateless license/use-gate check: given a license id + intended use, returns a conservative-heuristic verdict ' +\n '(allowed / allowed-with-attribution / denied / needs-review) with reasons and confidence. ' +\n 'Not legal advice — a strict-deny heuristic over source-declared license facts.',\n inputSchema: {\n license: z.enum(LICENSE_IDS).describe('the reference\\'s license id'),\n licenseVersion: z.string().optional().describe('precise CC version, e.g. \"4.0\" — attribution only'),\n author: z.string().optional(),\n title: z.string().optional(),\n canonicalUrl: z.string().describe('canonical source link, for attribution and audit'),\n intent: z.enum(INTENTS).describe('the intended use to evaluate this license against'),\n editorialOnly: z.boolean().optional().describe('source marked editorial-only'),\n jurisdiction: z.string().optional().describe('source-declared jurisdiction of the PD/copyright status'),\n userJurisdiction: z.string().optional().describe('caller\\'s jurisdiction; mismatched jurisdictions default to needs-review'),\n },\n outputSchema: {\n decision: z.enum(['allowed', 'allowed-with-attribution', 'denied', 'needs-review']),\n reasons: z.array(z.string()),\n confidence: z.enum(['high', 'low']),\n disclaimer: z.string(),\n attribution: z.object(attributionOutputSchema).optional(),\n },\n },\n async ({ license, licenseVersion, author, title, canonicalUrl, intent, editorialOnly, jurisdiction, userJurisdiction }) => {\n const version = ccVersionFor(license, licenseVersion)\n const rights: RightsRecord = {\n license,\n licenseVersion: version,\n author,\n rehostPolicy: 'cache-allowed',\n jurisdiction,\n editorialOnly,\n raw: { sourceTerms: '', sourceUrl: canonicalUrl },\n }\n const verdict: Verdict = evaluateUse(rights, intent, { userJurisdiction })\n const attribution =\n verdict.decision === 'allowed-with-attribution'\n ? buildAttribution({ license, licenseVersion: version, author, title, canonicalUrl })\n : undefined\n const summary = `${verdict.decision}: ${verdict.reasons.join('; ') || 'license facts allow this use'}`\n return {\n content: [{ type: 'text', text: summary }],\n structuredContent: {\n decision: verdict.decision,\n reasons: verdict.reasons,\n confidence: verdict.confidence,\n disclaimer: verdict.disclaimer,\n ...(attribution ? { attribution } : {}),\n },\n }\n },\n )\n\n server.registerTool(\n 'build_attribution',\n {\n title: 'Build an attribution credit line',\n description:\n 'Mechanically derive an attribution credit line (plain text + HTML) from a license id + author + title + ' +\n 'canonicalUrl. `required` is false when the license needs no attribution (e.g. CC0, PD).',\n inputSchema: {\n license: z.enum(LICENSE_IDS),\n licenseVersion: z.string().optional().describe('precise CC version, e.g. \"4.0\" — appended to the license name'),\n author: z.string().optional(),\n title: z.string().optional(),\n canonicalUrl: z.string(),\n },\n outputSchema: attributionOutputSchema,\n },\n async ({ license, licenseVersion, author, title, canonicalUrl }) => {\n const version = ccVersionFor(license, licenseVersion)\n const attribution: Attribution = buildAttribution({ license, licenseVersion: version, author, title, canonicalUrl })\n return {\n content: [{ type: 'text', text: attribution.required ? (attribution.text ?? '') : 'No attribution required for this license.' }],\n structuredContent: { required: attribution.required, text: attribution.text, html: attribution.html },\n }\n },\n )\n\n return server\n}\n\n/** Run the refkit MCP server over stdio (host wires the RefkitClient + its providers/keys). */\nexport async function serveStdio(refkit: RefkitClient): Promise<void> {\n const server = createRefkitMcpServer(refkit)\n await server.connect(new StdioServerTransport())\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AACrC,SAAS,SAAS;AAClB,SAAS,aAAa,SAAS,aAAa,kBAAkB,cAAc,uBAAuB;AAGnG,IAAM,aAAa,CAAC,SAAS,SAAS,SAAS,MAAM;AACrD,IAAM,eAAe,CAAC,aAAa,YAAY,QAAQ;AACvD,IAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,aAAa,EAAE,KAAK,YAAY,EAAE,SAAS;AAAA,EAC3C,UAAU,EAAE,OAAO,EAAE,SAAS;AAChC,CAAC;AACD,IAAM,yBAAyB,EAAE,KAAK,mBAAmB;AAEzD,IAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,aAAa,EAAE,KAAK,YAAY,EAAE,SAAS;AAAA,EAC3C,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,MAAM,EAAE,KAAK,CAAC,aAAa,UAAU,WAAW,aAAa,CAAC,EAAE,SAAS;AAAA,EACzE,QAAQ,EAAE,KAAK,CAAC,UAAU,YAAY,KAAK,CAAC,EAAE,SAAS;AAAA,EACvD,SAAS,EAAE,OAAO;AAAA,IAChB,YAAY,EAAE,QAAQ,EAAE,SAAS;AAAA,IACjC,cAAc,EAAE,QAAQ,EAAE,SAAS;AAAA,IACnC,cAAc,EAAE,QAAQ,EAAE,SAAS;AAAA,EACrC,CAAC,EAAE,SAAS;AAAA,EACZ,OAAO,EAAE,OAAO;AAAA,IACd,MAAM,EAAE,KAAK,CAAC,SAAS,gBAAgB,UAAU,QAAQ,WAAW,CAAC,EAAE,SAAS;AAAA,IAChF,MAAM,EAAE,KAAK,CAAC,SAAS,UAAU,OAAO,CAAC,EAAE,SAAS;AAAA,IACpD,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AAAA,IAClD,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AAAA,IACnD,UAAU,EAAE,KAAK,CAAC,SAAS,UAAU,MAAM,CAAC,EAAE,SAAS;AAAA,EACzD,CAAC,EAAE,SAAS;AAAA,EACZ,SAAS,EAAE,OAAO;AAAA,IAChB,IAAI,EAAE,OAAO,EAAE,SAAS;AAAA,IACxB,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,CAAC,EAAE,SAAS;AAAA,EACZ,MAAM,EAAE,OAAO;AAAA,IACb,WAAW,EAAE,KAAK,CAAC,iBAAiB,eAAe,KAAK,CAAC,EAAE,SAAS;AAAA,EACtE,CAAC,EAAE,SAAS;AAAA,EACZ,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAC7C,CAAC;AAED,IAAM,4BAA4B,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,GAAG,EAAE,QAAQ,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AACpG,IAAM,wBAAwB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,EAAE,OAAO,GAAG,yBAAyB,CAAC;AAIlG,IAAM,WAAmB,MAAM;AAC7B,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,MAAM,CAAC,EAAE;AAAA,EACvF,QAAQ;AACN,WAAO;AAAA,EACT;AACF,GAAG;AAKH,SAAS,WAAW,GAAc,YAA6D;AAC7F,QAAM,OAAO;AAAA,IACX,IAAI,EAAE;AAAA,IACN,OAAO,EAAE;AAAA,IACT,UAAU,EAAE;AAAA,IACZ,UAAU,EAAE,OAAO;AAAA,IACnB,cAAc,EAAE;AAAA,IAChB,SAAS,EAAE,OAAO;AAAA,IAClB,WAAW,EAAE,WAAW;AAAA,IACxB,SAAS,EAAE,MAAM;AAAA,EACnB;AACA,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,EAAE,SAAS,YAAY,IAAI;AACjC,QAAM,SAAS,QAAQ,QAAQ,KAAK,IAAI;AACxC,QAAM,iBAAiB,GAAG,QAAQ,QAAQ,KAAK,UAAU,8BAA8B,GAAG,YAAY,YAAY,YAAY,OAAO,0BAA0B,YAAY,IAAI,KAAK,EAAE;AACtL,SAAO;AAAA,IACL,GAAG;AAAA,IACH,YAAY,EAAE,UAAU,QAAQ,UAAU,QAAQ,QAAQ,QAAQ,KAAK,IAAI,GAAG,YAAY,QAAQ,WAAW;AAAA,IAC7G;AAAA,IACA,GAAI,YAAY,YAAY,YAAY,OAAO,EAAE,aAAa,YAAY,KAAK,IAAI,CAAC;AAAA,EACtF;AACF;AAEA,IAAM,iBAAiB,EAAE,OAAO;AAAA,EAC9B,IAAI,EAAE,OAAO;AAAA,EACb,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,UAAU,EAAE,OAAO;AAAA,EACnB,UAAU,EAAE,OAAO;AAAA,EACnB,cAAc,EAAE,OAAO;AAAA,EACvB,SAAS,EAAE,OAAO;AAAA,EAClB,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,YAAY,EACT,OAAO,EAAE,UAAU,EAAE,OAAO,GAAG,QAAQ,EAAE,OAAO,GAAG,YAAY,EAAE,OAAO,EAAE,CAAC,EAC3E,SAAS,EACT,SAAS,kGAAkG;AAAA,EAC9G,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+CAA+C;AAAA,EAC9F,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yEAAyE;AACvH,CAAC;AAED,IAAM,mBAA0C,EAAE,OAAO;AAAA,EACvD,OAAO,EAAE,OAAO;AAAA,EAChB,YAAY,EAAE,MAAM,EAAE,KAAK,UAAU,CAAC;AAAA,EACtC,OAAO,EAAE,OAAO;AAAA,EAChB,YAAY,EAAE,OAAO;AAAA,EACrB,YAAY,EAAE,OAAO;AAAA,EACrB,gBAAgB,cAAc,SAAS;AAAA,EACvC,UAAU,EAAE,OAAO;AAAA,IACjB,WAAW,EAAE,MAAM,sBAAsB;AAAA,IACzC,mBAAmB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAAA,IACvE,mBAAmB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAAA,EACzE,CAAC,EAAE,SAAS;AAAA,EACZ,iBAAiB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EAC9C,WAAW,EAAE,MAAM,EAAE,OAAO;AAAA,IAC1B,YAAY,EAAE,OAAO;AAAA,IACrB,QAAQ,EAAE,KAAK,CAAC,aAAa,UAAU,SAAS,CAAC;AAAA,IACjD,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,IAC9B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,IAC9B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,IAC9B,QAAQ,EAAE,KAAK,CAAC,sBAAsB,CAAC,EAAE,SAAS;AAAA,IAClD,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,IAC3B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,IAC/B,QAAQ,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC/B,CAAC,CAAC;AAAA,EACF,MAAM,EAAE,OAAO;AAAA,IACb,QAAQ,EAAE,KAAK,OAAO;AAAA,IACtB,QAAQ,EAAE,OAAO;AAAA,IACjB,OAAO,EAAE,OAAO;AAAA,IAChB,SAAS,EAAE,OAAO;AAAA,EACpB,CAAC,EAAE,SAAS;AAAA,EACZ,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6FAA6F;AAAA,EACxI,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC;AAC9B,CAAC;AAGM,SAAS,sBAAsB,QAAiC;AACrE,QAAM,SAAS,IAAI,UAAU,EAAE,MAAM,UAAU,SAAS,QAAQ,CAAC;AAEjE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAIF,aAAa;AAAA,QACX,OAAO,EAAE,OAAO,EAAE,SAAS,qDAAqD;AAAA,QAChF,YAAY,EAAE,MAAM,EAAE,KAAK,UAAU,CAAC,EAAE,SAAS,EAAE,SAAS,mBAAmB;AAAA,QAC/E,SAAS,cAAc,SAAS,EAAE,SAAS,qFAAqF;AAAA,QAChI,UAAU,qBAAqB,SAAS,EAAE,SAAS,0HAA0H;AAAA,QAC7K,iBAAiB,sBAAsB,SAAS,EAAE,SAAS,iGAAiG;AAAA,QAC5J,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,+GAA+G;AAAA,QACxJ,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,QAC5C,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kHAA8G;AAAA,QACrJ,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,oIAAoI;AAAA,QAC5K,QAAQ,EAAE,KAAK,OAAO,EAAE,SAAS,EAAE,SAAS,8EAA8E;AAAA,QAC1H,SAAS,EAAE,KAAK,OAAO,EAAE,SAAS,EAAE,SAAS,4DAA4D;AAAA,MAC3G;AAAA,MACA,cAAc;AAAA,QACZ,YAAY,EAAE,MAAM,cAAc;AAAA,QAClC,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8DAA8D;AAAA,QACzG,MAAM,iBAAiB,SAAS;AAAA,MAClC;AAAA,IACF;AAAA,IACA,OAAO,EAAE,OAAO,YAAY,SAAS,UAAU,iBAAiB,SAAS,OAAO,QAAQ,QAAQ,QAAQ,QAAQ,MAAM;AACpH,YAAM,cAAc;AAAA,QAClB;AAAA,QACA,YAAY,cAAc,CAAC,OAAO;AAAA,QAClC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,SAAS,EAAE,QAAQ,gBAAgB,EAAE,IAAI,CAAC;AAAA,QAC9C;AAAA,MACF;AAGA,YAAM,SAAS,MAAM,OAAO,eAAe,WAAW;AACtD,YAAM,OAAO,OAAO;AACpB,YAAM,eAAe,UAAU;AAC/B,YAAM,aAAa,KAAK;AAAA,QAAI,OAC1B,eACI,WAAW,GAAG,EAAE,SAAS,OAAO,YAAY,GAAG,YAAY,GAAG,aAAa,OAAO,iBAAiB,CAAC,EAAE,CAAC,IACvG,WAAW,CAAC;AAAA,MAClB;AACA,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,GAAG,WAAW,MAAM,sBAAsB,KAAK,KAAK,CAAC;AAAA,QACrF,mBAAmB;AAAA,UACjB;AAAA,UACA,GAAI,OAAO,KAAK,aAAa,EAAE,YAAY,OAAO,KAAK,WAAW,IAAI,CAAC;AAAA,UACvE,GAAI,UAAU,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,0BAA0B,EAAE,UAAU,EAAE,QAAQ,GAAG,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE;AAElH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa;AAAA,QACX,SAAS,EAAE,KAAK,WAAW,EAAE,SAAS,4BAA6B;AAAA,QACnE,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wDAAmD;AAAA,QAClG,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,QAC5B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,QAC3B,cAAc,EAAE,OAAO,EAAE,SAAS,kDAAkD;AAAA,QACpF,QAAQ,EAAE,KAAK,OAAO,EAAE,SAAS,mDAAmD;AAAA,QACpF,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,8BAA8B;AAAA,QAC7E,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yDAAyD;AAAA,QACtG,kBAAkB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yEAA0E;AAAA,MAC7H;AAAA,MACA,cAAc;AAAA,QACZ,UAAU,EAAE,KAAK,CAAC,WAAW,4BAA4B,UAAU,cAAc,CAAC;AAAA,QAClF,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,QAC3B,YAAY,EAAE,KAAK,CAAC,QAAQ,KAAK,CAAC;AAAA,QAClC,YAAY,EAAE,OAAO;AAAA,QACrB,aAAa,EAAE,OAAO,uBAAuB,EAAE,SAAS;AAAA,MAC1D;AAAA,IACF;AAAA,IACA,OAAO,EAAE,SAAS,gBAAgB,QAAQ,OAAO,cAAc,QAAQ,eAAe,cAAc,iBAAiB,MAAM;AACzH,YAAM,UAAU,aAAa,SAAS,cAAc;AACpD,YAAM,SAAuB;AAAA,QAC3B;AAAA,QACA,gBAAgB;AAAA,QAChB;AAAA,QACA,cAAc;AAAA,QACd;AAAA,QACA;AAAA,QACA,KAAK,EAAE,aAAa,IAAI,WAAW,aAAa;AAAA,MAClD;AACA,YAAM,UAAmB,YAAY,QAAQ,QAAQ,EAAE,iBAAiB,CAAC;AACzE,YAAM,cACJ,QAAQ,aAAa,6BACjB,iBAAiB,EAAE,SAAS,gBAAgB,SAAS,QAAQ,OAAO,aAAa,CAAC,IAClF;AACN,YAAM,UAAU,GAAG,QAAQ,QAAQ,KAAK,QAAQ,QAAQ,KAAK,IAAI,KAAK,8BAA8B;AACpG,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,QACzC,mBAAmB;AAAA,UACjB,UAAU,QAAQ;AAAA,UAClB,SAAS,QAAQ;AAAA,UACjB,YAAY,QAAQ;AAAA,UACpB,YAAY,QAAQ;AAAA,UACpB,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa;AAAA,QACX,SAAS,EAAE,KAAK,WAAW;AAAA,QAC3B,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oEAA+D;AAAA,QAC9G,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,QAC5B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,QAC3B,cAAc,EAAE,OAAO;AAAA,MACzB;AAAA,MACA,cAAc;AAAA,IAChB;AAAA,IACA,OAAO,EAAE,SAAS,gBAAgB,QAAQ,OAAO,aAAa,MAAM;AAClE,YAAM,UAAU,aAAa,SAAS,cAAc;AACpD,YAAM,cAA2B,iBAAiB,EAAE,SAAS,gBAAgB,SAAS,QAAQ,OAAO,aAAa,CAAC;AACnH,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,YAAY,WAAY,YAAY,QAAQ,KAAM,4CAA4C,CAAC;AAAA,QAC/H,mBAAmB,EAAE,UAAU,YAAY,UAAU,MAAM,YAAY,MAAM,MAAM,YAAY,KAAK;AAAA,MACtG;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGA,eAAsB,WAAW,QAAqC;AACpE,QAAM,SAAS,sBAAsB,MAAM;AAC3C,QAAM,OAAO,QAAQ,IAAI,qBAAqB,CAAC;AACjD;","names":[]}
|