freshcontext-mcp 0.3.20 → 0.3.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +55 -9
  2. package/SECURITY.md +9 -7
  3. package/dist/adapters/arxiv.d.ts +15 -0
  4. package/dist/adapters/arxiv.js +1 -1
  5. package/dist/adapters/changelog.d.ts +2 -0
  6. package/dist/adapters/finance.d.ts +2 -0
  7. package/dist/adapters/finance.js +1 -1
  8. package/dist/adapters/gdelt.d.ts +2 -0
  9. package/dist/adapters/gdelt.js +1 -1
  10. package/dist/adapters/gebiz.d.ts +2 -0
  11. package/dist/adapters/gebiz.js +1 -1
  12. package/dist/adapters/github.d.ts +2 -0
  13. package/dist/adapters/govcontracts.d.ts +2 -0
  14. package/dist/adapters/hackernews.d.ts +2 -0
  15. package/dist/adapters/jobs.d.ts +2 -0
  16. package/dist/adapters/jobs.js +6 -6
  17. package/dist/adapters/packageTrends.d.ts +2 -0
  18. package/dist/adapters/productHunt.d.ts +2 -0
  19. package/dist/adapters/reddit.d.ts +8 -0
  20. package/dist/adapters/reddit.js +1 -1
  21. package/dist/adapters/registry.d.ts +19 -0
  22. package/dist/adapters/repoSearch.d.ts +2 -0
  23. package/dist/adapters/repoSearch.js +1 -1
  24. package/dist/adapters/scholar.d.ts +2 -0
  25. package/dist/adapters/secFilings.d.ts +2 -0
  26. package/dist/adapters/secFilings.js +1 -1
  27. package/dist/adapters/yc.d.ts +2 -0
  28. package/dist/core/decay.d.ts +5 -0
  29. package/dist/core/decision.d.ts +3 -0
  30. package/dist/core/decision.js +1 -3
  31. package/dist/core/envelope.d.ts +5 -0
  32. package/dist/core/explain.d.ts +12 -0
  33. package/dist/core/guards.d.ts +1 -0
  34. package/dist/core/index.d.ts +14 -0
  35. package/dist/core/index.js +2 -0
  36. package/dist/core/pipeline.d.ts +3 -0
  37. package/dist/core/pipeline.js +8 -0
  38. package/dist/core/provenance.d.ts +5 -0
  39. package/dist/core/provenanceReadiness.d.ts +2 -0
  40. package/dist/core/provenanceReadiness.js +220 -0
  41. package/dist/core/rank.d.ts +4 -0
  42. package/dist/core/readable.d.ts +2 -0
  43. package/dist/core/readable.js +75 -0
  44. package/dist/core/signal.d.ts +3 -0
  45. package/dist/core/sourceProfiles.d.ts +4 -0
  46. package/dist/core/types.d.ts +239 -0
  47. package/dist/core/utility.d.ts +2 -0
  48. package/dist/rest/handler.d.ts +1 -0
  49. package/dist/security.d.ts +15 -0
  50. package/dist/server.d.ts +2 -0
  51. package/dist/server.js +1 -1
  52. package/dist/tools/evaluateContext.d.ts +21 -0
  53. package/dist/tools/evaluateContext.js +3 -1
  54. package/dist/tools/freshnessStamp.d.ts +1 -0
  55. package/dist/types.d.ts +1 -0
  56. package/docs/API_DESIGN.md +28 -1
  57. package/docs/CLIENT_SETUP.md +4 -4
  58. package/docs/CODEX_MCP_USAGE.md +3 -3
  59. package/docs/CORE_API.md +74 -12
  60. package/docs/CORE_MCP_BOUNDARY.md +13 -4
  61. package/docs/FUTURE_LANES.md +43 -43
  62. package/docs/HUMAN_READABLE_OUTPUT_CONTRACT.md +293 -0
  63. package/docs/RELEASE_NOTES.md +33 -5
  64. package/docs/SIGNAL_CONTRACT.md +215 -213
  65. package/package-script-guard.mjs +84 -75
  66. package/package.json +31 -16
  67. package/server.json +2 -2
@@ -0,0 +1,220 @@
1
+ import { calculateHaPriV2, canonicalizeHaPriContent, sha256Hex } from "./provenance.js";
2
+ import { SIGNAL_CONTRACT_VERSION, normalizeSignal } from "./signal.js";
3
+ const UNUSABLE_SOURCE_VALUES = new Set([
4
+ "unknown",
5
+ "n/a",
6
+ "na",
7
+ "none",
8
+ "null",
9
+ "undefined",
10
+ "not provided",
11
+ "tbd",
12
+ ]);
13
+ const WEAK_SOURCE_TYPES = new Set(["", "default", "unknown", "custom"]);
14
+ const DERIVED_SOURCE_TYPES = new Set([
15
+ "copy",
16
+ "copied",
17
+ "derived",
18
+ "excerpt",
19
+ "local",
20
+ "local_custom",
21
+ "local_file",
22
+ "local_handoff",
23
+ "secondary",
24
+ "summary",
25
+ ]);
26
+ function cleanString(value) {
27
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
28
+ }
29
+ function unique(values) {
30
+ return [...new Set(values.filter(Boolean))];
31
+ }
32
+ function isNormalizedSignal(input) {
33
+ return input.contract_version === SIGNAL_CONTRACT_VERSION;
34
+ }
35
+ function coerceInput(input) {
36
+ const value = input;
37
+ return {
38
+ ...value,
39
+ source: cleanString(value.source) ?? "",
40
+ };
41
+ }
42
+ function metadataString(signal, keys) {
43
+ for (const key of keys) {
44
+ const value = signal.metadata[key];
45
+ if (typeof value === "string" && value.trim().length > 0)
46
+ return value.trim();
47
+ }
48
+ return null;
49
+ }
50
+ function metadataBoolean(signal, keys) {
51
+ return keys.some((key) => signal.metadata[key] === true);
52
+ }
53
+ function hasStringField(input, field) {
54
+ return cleanString(input[field]) !== null;
55
+ }
56
+ function sourceIdentityCompleteness(source) {
57
+ if (!source)
58
+ return "missing";
59
+ const lower = source.toLowerCase();
60
+ if (UNUSABLE_SOURCE_VALUES.has(lower))
61
+ return "unusable";
62
+ if (source.length < 4)
63
+ return "weak";
64
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(source))
65
+ return "complete";
66
+ if (/^[a-z][a-z0-9+.-]*:/i.test(source))
67
+ return "complete";
68
+ if (/^[a-z]:\\/i.test(source) || source.startsWith("/") || source.startsWith("\\\\"))
69
+ return "complete";
70
+ if (source.includes("/") || source.includes("\\") || source.includes("."))
71
+ return "complete";
72
+ return source.length >= 12 ? "complete" : "weak";
73
+ }
74
+ function isDerivedContext(signal) {
75
+ const sourceType = signal.source_type.toLowerCase();
76
+ if (DERIVED_SOURCE_TYPES.has(sourceType))
77
+ return true;
78
+ if (metadataBoolean(signal, ["is_derived", "derived", "copied", "is_secondary"]))
79
+ return true;
80
+ return metadataString(signal, [
81
+ "derived_from",
82
+ "copied_from",
83
+ "original_source",
84
+ "origin_source",
85
+ "source_chain",
86
+ ]) !== null;
87
+ }
88
+ function timingCompleteness(signal, retrievedAtWasProvided) {
89
+ if (signal.status === "failed")
90
+ return "unknown";
91
+ if (!signal.published_at && !signal.retrieved_at)
92
+ return "missing";
93
+ if (!signal.published_at || signal.date_confidence === "unknown" || retrievedAtWasProvided === false) {
94
+ return "partial";
95
+ }
96
+ return "complete";
97
+ }
98
+ function readinessState(input) {
99
+ if (input.signal.status === "failed" || input.sourceCompleteness === "unusable")
100
+ return "unknown";
101
+ if (input.derived)
102
+ return "derived";
103
+ if (input.sourceCompleteness === "missing" || !input.hasCanonicalHash)
104
+ return "incomplete";
105
+ if (input.sourceCompleteness === "complete"
106
+ && input.timingCompleteness === "complete"
107
+ && !input.sourceTypeWeak) {
108
+ return "complete";
109
+ }
110
+ return "partial";
111
+ }
112
+ function canonicalContentHash(signal) {
113
+ return signal.content === undefined ? null : sha256Hex(canonicalizeHaPriContent(signal.content));
114
+ }
115
+ function prepareHaPriV2(input) {
116
+ if (input.signal.status === "failed") {
117
+ input.reasons.push("Ha-Pri v2 identity material was not prepared for failed context");
118
+ return null;
119
+ }
120
+ if (!input.signal.content) {
121
+ input.reasons.push("Ha-Pri v2 identity material needs content");
122
+ return null;
123
+ }
124
+ if (!input.resultId) {
125
+ input.reasons.push("Ha-Pri v2 identity material needs a result id or stable source identity");
126
+ return null;
127
+ }
128
+ if (!input.engineVersion) {
129
+ input.reasons.push("Ha-Pri v2 identity material needs an explicit engine version");
130
+ return null;
131
+ }
132
+ input.reasons.push("Ha-Pri v2 identity material prepared from caller-provided context");
133
+ return calculateHaPriV2({
134
+ resultId: input.resultId,
135
+ rawContent: input.signal.content,
136
+ semanticFingerprint: input.semanticFingerprint,
137
+ adapter: input.signal.source_type,
138
+ publishedAt: input.signal.published_at,
139
+ retrievedAt: input.signal.retrieved_at,
140
+ engineVersion: input.engineVersion,
141
+ });
142
+ }
143
+ export function prepareProvenanceReadiness(input, options = {}) {
144
+ const normalizedInput = isNormalizedSignal(input);
145
+ const signal = normalizedInput ? input : normalizeSignal(coerceInput(input), options);
146
+ const warnings = [];
147
+ const reasons = [...signal.reasons];
148
+ const source = cleanString(signal.source);
149
+ const sourceCompleteness = sourceIdentityCompleteness(source);
150
+ const sourceType = cleanString(signal.source_type);
151
+ const sourceTypeWeak = !sourceType || WEAK_SOURCE_TYPES.has(sourceType.toLowerCase());
152
+ const retrievedAtWasProvided = normalizedInput ? null : hasStringField(input, "retrieved_at");
153
+ const timeCompleteness = timingCompleteness(signal, retrievedAtWasProvided);
154
+ const canonicalHash = canonicalContentHash(signal);
155
+ const semanticFingerprint = cleanString(options.semanticFingerprint)
156
+ ?? metadataString(signal, ["semantic_fingerprint", "semanticFingerprint"]);
157
+ const semanticFingerprintHash = semanticFingerprint ? sha256Hex(semanticFingerprint) : null;
158
+ const resultId = cleanString(options.resultId)
159
+ ?? cleanString(signal.id)
160
+ ?? metadataString(signal, ["result_id", "resultId", "source_id", "sourceId"])
161
+ ?? (sourceCompleteness === "complete" ? source : null);
162
+ const engineVersion = cleanString(options.engineVersion);
163
+ const derived = isDerivedContext(signal);
164
+ if (sourceCompleteness === "missing")
165
+ warnings.push("source identity is missing");
166
+ if (sourceCompleteness === "weak")
167
+ warnings.push("source identity is weak");
168
+ if (sourceCompleteness === "unusable")
169
+ warnings.push("source identity is unusable");
170
+ if (sourceTypeWeak)
171
+ warnings.push("source_type is missing or generic");
172
+ if (!signal.published_at)
173
+ warnings.push("published_at is missing or unusable");
174
+ if (retrievedAtWasProvided === false)
175
+ warnings.push("retrieved_at was missing; normalization time was used");
176
+ if (signal.date_confidence === "unknown")
177
+ warnings.push("timing confidence is unknown");
178
+ if (!canonicalHash)
179
+ warnings.push("content is missing; canonical content hash is unavailable");
180
+ if (!semanticFingerprintHash)
181
+ reasons.push("semantic fingerprint was not provided");
182
+ if (derived)
183
+ warnings.push("context appears copied, local, secondary, or derived; preserve the upstream source chain");
184
+ if (signal.status === "failed")
185
+ warnings.push("failed context has unknown provenance readiness");
186
+ const haPriV2 = prepareHaPriV2({
187
+ signal,
188
+ resultId,
189
+ semanticFingerprint,
190
+ engineVersion,
191
+ reasons,
192
+ });
193
+ const state = readinessState({
194
+ signal,
195
+ sourceCompleteness,
196
+ timingCompleteness: timeCompleteness,
197
+ sourceTypeWeak,
198
+ hasCanonicalHash: canonicalHash !== null,
199
+ derived,
200
+ });
201
+ return {
202
+ state,
203
+ source_identity: {
204
+ source,
205
+ source_type: sourceType,
206
+ result_id: resultId,
207
+ completeness: sourceCompleteness,
208
+ },
209
+ source_type: sourceType,
210
+ published_at: signal.published_at,
211
+ retrieved_at: signal.retrieved_at,
212
+ timing_confidence: signal.date_confidence,
213
+ timing_completeness: timeCompleteness,
214
+ canonical_content_sha256: canonicalHash,
215
+ semantic_fingerprint_sha256: semanticFingerprintHash,
216
+ ha_pri_v2: haPriV2,
217
+ warnings: unique(warnings),
218
+ reasons: unique(reasons),
219
+ };
220
+ }
@@ -0,0 +1,4 @@
1
+ import type { FreshSignal, RankOptions, RankedSignal } from "./types.js";
2
+ export declare function clampScore(value: number): number;
3
+ export declare function rankSignal(signal: FreshSignal, options?: RankOptions): RankedSignal;
4
+ export declare function rankSignals(signals: FreshSignal[], options?: RankOptions): RankedSignal[];
@@ -0,0 +1,2 @@
1
+ import type { ContextDecisionResult, CoreSignalEvaluationResult, HumanReadableContextResult } from "./types.js";
2
+ export declare function toReadableContextResult(evaluation: CoreSignalEvaluationResult, decision: ContextDecisionResult): HumanReadableContextResult;
@@ -0,0 +1,75 @@
1
+ const MAX_READABLE_REASONS = 5;
2
+ const HANDOFF_SAFE_REASON = "Decision and complete provenance support agent handoff.";
3
+ const HANDOFF_UNSAFE_DECISION_REASON = "Decision does not support agent handoff.";
4
+ const HANDOFF_UNSAFE_PROVENANCE_REASON = "Provenance is not complete enough for agent handoff.";
5
+ const HANDOFF_SAFE_DECISIONS = new Set([
6
+ "use_first",
7
+ "cite_as_primary",
8
+ "cite_as_supporting",
9
+ "use_as_background",
10
+ ]);
11
+ const READER_LABELS = {
12
+ use_first: "Use first",
13
+ cite_as_primary: "Primary source",
14
+ cite_as_supporting: "Supporting source",
15
+ use_as_background: "Background only",
16
+ needs_verification: "Needs verification",
17
+ needs_refresh: "Needs refresh",
18
+ watch_only: "Watch only",
19
+ exclude: "Excluded",
20
+ };
21
+ const SUMMARIES = {
22
+ use_first: "This source is strong enough to use early in the context bundle.",
23
+ cite_as_primary: "This source is strong enough to use as main evidence.",
24
+ cite_as_supporting: "This source is useful as supporting evidence.",
25
+ use_as_background: "This source is useful as background, but should not carry the main claim.",
26
+ needs_verification: "This source may be relevant, but timing, origin, or confidence needs checking.",
27
+ needs_refresh: "This source may be outdated for the current task and should be refreshed.",
28
+ watch_only: "This source may be related, but is too weak to rely on directly.",
29
+ exclude: "This source should not be included as trusted context.",
30
+ };
31
+ const ACTIONS = {
32
+ use_first: "Use this near the front of the selected context while preserving provenance.",
33
+ cite_as_primary: "Use this as main evidence while preserving citation and provenance.",
34
+ cite_as_supporting: "Use this to support or qualify the answer, not as the only source.",
35
+ use_as_background: "Keep this as background context only.",
36
+ needs_verification: "Do not use this as primary evidence until it is checked.",
37
+ needs_refresh: "Look for a newer source before using this as current evidence.",
38
+ watch_only: "Do not rely on this directly; keep it for monitoring or review.",
39
+ exclude: "Keep this out of the final model context unless a human reviews it.",
40
+ };
41
+ function unique(values) {
42
+ return [...new Set(values.filter(Boolean))];
43
+ }
44
+ function handoffFor(evaluation, decision) {
45
+ if (!HANDOFF_SAFE_DECISIONS.has(decision.decision)) {
46
+ return {
47
+ safe_for_agent_handoff: false,
48
+ reason: HANDOFF_UNSAFE_DECISION_REASON,
49
+ };
50
+ }
51
+ if (evaluation.provenance_readiness.state !== "complete") {
52
+ return {
53
+ safe_for_agent_handoff: false,
54
+ reason: HANDOFF_UNSAFE_PROVENANCE_REASON,
55
+ };
56
+ }
57
+ return {
58
+ safe_for_agent_handoff: true,
59
+ reason: HANDOFF_SAFE_REASON,
60
+ };
61
+ }
62
+ export function toReadableContextResult(evaluation, decision) {
63
+ const why = unique([
64
+ ...decision.reasons,
65
+ evaluation.explanation,
66
+ ]).slice(0, MAX_READABLE_REASONS);
67
+ return {
68
+ label: READER_LABELS[decision.decision],
69
+ summary: SUMMARIES[decision.decision],
70
+ why,
71
+ action: ACTIONS[decision.decision],
72
+ warnings: [...decision.warnings],
73
+ handoff: handoffFor(evaluation, decision),
74
+ };
75
+ }
@@ -0,0 +1,3 @@
1
+ import type { FreshContextSignal, FreshContextSignalInput, SignalNormalizeOptions } from "./types.js";
2
+ export declare const SIGNAL_CONTRACT_VERSION: "freshcontext.signal.v1";
3
+ export declare function normalizeSignal(input: FreshContextSignalInput, options?: SignalNormalizeOptions): FreshContextSignal;
@@ -0,0 +1,4 @@
1
+ import type { SourceProfile, SourceProfileId } from "./types.js";
2
+ export declare const BUILT_IN_SOURCE_PROFILES: Readonly<Record<SourceProfileId, SourceProfile>>;
3
+ export declare function listSourceProfiles(): SourceProfile[];
4
+ export declare function getSourceProfile(profileId: string): SourceProfile | undefined;
@@ -0,0 +1,239 @@
1
+ export interface FreshContext {
2
+ content: string;
3
+ source_url: string;
4
+ content_date: string | null;
5
+ retrieved_at: string;
6
+ freshness_confidence: "high" | "medium" | "low";
7
+ freshness_score: number | null;
8
+ adapter: string;
9
+ }
10
+ export interface ExtractOptions {
11
+ url: string;
12
+ prompt?: string;
13
+ maxLength?: number;
14
+ location?: string;
15
+ remoteOnly?: boolean;
16
+ maxAgeDays?: number;
17
+ keywords?: string[];
18
+ }
19
+ export interface AdapterResult {
20
+ raw: string;
21
+ content_date: string | null;
22
+ freshness_confidence: "high" | "medium" | "low";
23
+ }
24
+ export interface EnvelopeFormatOptions {
25
+ unknownDateText?: string;
26
+ publishedLabel?: string;
27
+ }
28
+ export type SignalConfidence = "high" | "medium" | "low";
29
+ export type SignalDateConfidence = SignalConfidence | "unknown";
30
+ export type SignalContractVersion = "freshcontext.signal.v1";
31
+ export type SourceAuthorityHint = "high" | "medium" | "low";
32
+ export type SourceDatePolicy = "strict" | "balanced" | "lenient";
33
+ export type SourceFailurePolicy = "exclude" | "downgrade" | "warn";
34
+ export type SourceSurface = "mcp" | "rest" | "sdk" | "cli" | "operator";
35
+ export type SourceProfileId = "official_docs" | "code_activity" | "social_pulse" | "academic_research" | "market_finance" | "jobs_opportunities" | "government_regulatory" | "company_intel" | "composite_landscape" | "local_custom";
36
+ export type ContextDecision = "use_first" | "cite_as_primary" | "cite_as_supporting" | "use_as_background" | "needs_verification" | "needs_refresh" | "watch_only" | "exclude";
37
+ export type IntentProfileId = "citation_check" | "student_research" | "developer_adoption" | "job_search" | "market_watch" | "business_due_diligence" | "medical_literature_triage";
38
+ export type ContextUtilityStatus = "success" | "partial" | "stale" | "failed" | "unknown";
39
+ export interface SignalNormalizeOptions {
40
+ defaultSourceType?: string;
41
+ now?: Date | string;
42
+ }
43
+ export interface SourceProfile {
44
+ profile_id: SourceProfileId;
45
+ source_types: string[];
46
+ purpose: string;
47
+ default_decay_lambda: number;
48
+ half_life_hours: number;
49
+ authority_hint: SourceAuthorityHint;
50
+ date_policy: SourceDatePolicy;
51
+ failure_policy: SourceFailurePolicy;
52
+ recommended_surfaces: SourceSurface[];
53
+ }
54
+ export interface ContextDecisionOptions {
55
+ sourceProfile?: SourceProfile | SourceProfileId;
56
+ intentProfile?: IntentProfileId;
57
+ }
58
+ export interface ContextDecisionResult {
59
+ decision: ContextDecision;
60
+ label: string;
61
+ meaning: string;
62
+ action: string;
63
+ reasons: string[];
64
+ warnings: string[];
65
+ }
66
+ export interface HumanReadableHandoffResult {
67
+ safe_for_agent_handoff: boolean;
68
+ reason: string;
69
+ }
70
+ export interface HumanReadableContextResult {
71
+ label: string;
72
+ summary: string;
73
+ why: string[];
74
+ action: string;
75
+ warnings: string[];
76
+ handoff: HumanReadableHandoffResult;
77
+ }
78
+ export interface FreshContextSignalInput {
79
+ id?: string;
80
+ source: string;
81
+ source_type?: string;
82
+ title?: string;
83
+ content?: string;
84
+ published_at?: string | null;
85
+ content_date?: string | null;
86
+ retrieved_at?: string | null;
87
+ semantic_score?: number;
88
+ date_confidence?: SignalDateConfidence;
89
+ freshness_confidence?: SignalConfidence;
90
+ status?: ContextUtilityStatus;
91
+ metadata?: Record<string, unknown>;
92
+ }
93
+ export interface FreshContextSignal {
94
+ contract_version: SignalContractVersion;
95
+ id?: string;
96
+ source: string;
97
+ source_type: string;
98
+ title?: string;
99
+ content?: string;
100
+ published_at: string | null;
101
+ retrieved_at: string;
102
+ semantic_score: number;
103
+ date_confidence: SignalDateConfidence;
104
+ status: ContextUtilityStatus;
105
+ metadata: Record<string, unknown>;
106
+ reasons: string[];
107
+ }
108
+ export interface FreshSignal {
109
+ id?: string;
110
+ source: string;
111
+ source_type?: string;
112
+ title?: string;
113
+ content?: string;
114
+ published_at?: string | null;
115
+ content_date?: string | null;
116
+ retrieved_at?: string | null;
117
+ semantic_score: number;
118
+ date_confidence?: SignalDateConfidence;
119
+ freshness_confidence?: SignalConfidence;
120
+ status?: ContextUtilityStatus;
121
+ metadata?: Record<string, unknown>;
122
+ }
123
+ export interface RankedSignal extends FreshSignal {
124
+ freshness_score: number | null;
125
+ final_score: number;
126
+ confidence: SignalConfidence;
127
+ reason: string;
128
+ }
129
+ export interface RankOptions {
130
+ semanticWeight?: number;
131
+ freshnessWeight?: number;
132
+ defaultSourceType?: string;
133
+ now?: Date | string;
134
+ }
135
+ export interface ContextUtilityInput {
136
+ contextualRelevance: number;
137
+ lambda: number;
138
+ ageHours: number;
139
+ dateConfidence?: SignalConfidence | "unknown";
140
+ status?: ContextUtilityStatus;
141
+ }
142
+ export interface ContextUtilityResult {
143
+ score: number;
144
+ contextualRelevance: number;
145
+ decayFactor: number;
146
+ dateConfidenceFactor: number;
147
+ statusFactor: number;
148
+ lambda: number;
149
+ ageHours: number;
150
+ status: ContextUtilityStatus;
151
+ reasons: string[];
152
+ }
153
+ export interface HaPriV2Input {
154
+ resultId: string;
155
+ rawContent: string;
156
+ semanticFingerprint?: string | null;
157
+ adapter: string;
158
+ publishedAt?: string | null;
159
+ retrievedAt?: string | null;
160
+ engineVersion: string;
161
+ }
162
+ export interface HaPriV2Material {
163
+ version: "FRESHCONTEXT_HA_PRI_V2";
164
+ resultId: string;
165
+ canonicalContentSha256: string;
166
+ semanticFingerprintSha256: string;
167
+ adapter: string;
168
+ publishedAt: string;
169
+ retrievedAt: string;
170
+ engineVersion: string;
171
+ signingPayload: string;
172
+ }
173
+ export interface HaPriV2Result extends HaPriV2Material {
174
+ haPriSigV2: string;
175
+ }
176
+ export type HaPriVerificationStatus = "valid" | "invalid" | "unknown";
177
+ export interface HaPriV2VerificationResult {
178
+ status: HaPriVerificationStatus;
179
+ expected: string | null;
180
+ actual: string | null;
181
+ reasons: string[];
182
+ }
183
+ export type ProvenanceReadinessState = "complete" | "partial" | "incomplete" | "unknown" | "derived";
184
+ export type ProvenanceSourceIdentityCompleteness = "complete" | "weak" | "missing" | "unusable";
185
+ export type ProvenanceTimingCompleteness = "complete" | "partial" | "missing" | "unknown";
186
+ export type ProvenanceReadinessInput = Partial<FreshContextSignalInput> | FreshContextSignal;
187
+ export interface ProvenanceReadinessOptions extends SignalNormalizeOptions {
188
+ resultId?: string;
189
+ semanticFingerprint?: string | null;
190
+ engineVersion?: string;
191
+ }
192
+ export interface ProvenanceSourceIdentityResult {
193
+ source: string | null;
194
+ source_type: string | null;
195
+ result_id: string | null;
196
+ completeness: ProvenanceSourceIdentityCompleteness;
197
+ }
198
+ export interface ProvenanceReadinessResult {
199
+ state: ProvenanceReadinessState;
200
+ source_identity: ProvenanceSourceIdentityResult;
201
+ source_type: string | null;
202
+ published_at: string | null;
203
+ retrieved_at: string | null;
204
+ timing_confidence: SignalDateConfidence;
205
+ timing_completeness: ProvenanceTimingCompleteness;
206
+ canonical_content_sha256: string | null;
207
+ semantic_fingerprint_sha256: string | null;
208
+ ha_pri_v2: HaPriV2Result | null;
209
+ warnings: string[];
210
+ reasons: string[];
211
+ }
212
+ export interface CoreSignalProvenanceOptions {
213
+ resultId?: string;
214
+ semanticFingerprint?: string | null;
215
+ engineVersion?: string;
216
+ }
217
+ export interface CoreSignalEnvelopeResult {
218
+ context: FreshContext;
219
+ text: string;
220
+ structured: object;
221
+ }
222
+ export interface CoreSignalEvaluationOptions extends SignalNormalizeOptions, RankOptions {
223
+ includeEnvelope?: boolean;
224
+ envelopeMaxLength?: number;
225
+ envelopeFormat?: EnvelopeFormatOptions;
226
+ includeProvenance?: boolean;
227
+ provenance?: CoreSignalProvenanceOptions;
228
+ }
229
+ export interface CoreSignalEvaluationResult {
230
+ signal: FreshContextSignal;
231
+ freshness_score: number | null;
232
+ utility: ContextUtilityResult;
233
+ ranked: RankedSignal;
234
+ explanation: string;
235
+ envelope?: CoreSignalEnvelopeResult;
236
+ provenance?: HaPriV2Result;
237
+ provenance_readiness: ProvenanceReadinessResult;
238
+ reasons: string[];
239
+ }
@@ -0,0 +1,2 @@
1
+ import type { ContextUtilityInput, ContextUtilityResult } from "./types.js";
2
+ export declare function calculateContextUtility(input: ContextUtilityInput): ContextUtilityResult;
@@ -0,0 +1 @@
1
+ export declare function handleRestRequest(request: Request): Promise<Response>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * freshcontext-mcp security module
3
+ * Input sanitization, domain allowlists, and request validation
4
+ */
5
+ export declare const ALLOWED_DOMAINS: Record<string, string[]>;
6
+ export declare const MAX_URL_LENGTH = 500;
7
+ export declare const MAX_QUERY_LENGTH = 200;
8
+ export declare const MAX_PACKAGES_LENGTH = 300;
9
+ export declare class SecurityError extends Error {
10
+ constructor(message: string);
11
+ }
12
+ export declare function validateUrl(rawUrl: string, adapterName: keyof typeof ALLOWED_DOMAINS): string;
13
+ export declare function sanitizeQuery(query: string, maxLength?: number): string;
14
+ export declare function sanitizePackages(input: string): string;
15
+ export declare function formatSecurityError(err: unknown): string;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/server.js CHANGED
@@ -23,7 +23,7 @@ import { EvaluateContextInputError, evaluateContextInput, formatEvaluateContextR
23
23
  import { formatSecurityError } from "./security.js";
24
24
  const server = new McpServer({
25
25
  name: "freshcontext-mcp",
26
- version: "0.3.20",
26
+ version: "0.3.21",
27
27
  });
28
28
  const signalInputSchema = z.object({
29
29
  id: z.string().optional(),
@@ -0,0 +1,21 @@
1
+ import type { ContextDecisionResult, CoreSignalEvaluationResult, IntentProfileId, SourceProfile } from "../core/index.js";
2
+ export declare class EvaluateContextInputError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export interface EvaluateContextInput {
6
+ profile: string;
7
+ intent: string;
8
+ signals: unknown;
9
+ now?: string;
10
+ }
11
+ export interface EvaluateContextItem {
12
+ evaluation: CoreSignalEvaluationResult;
13
+ decision: ContextDecisionResult;
14
+ }
15
+ export interface EvaluateContextResult {
16
+ profile: SourceProfile;
17
+ intent: IntentProfileId;
18
+ items: EvaluateContextItem[];
19
+ }
20
+ export declare function evaluateContextInput(input: EvaluateContextInput): EvaluateContextResult;
21
+ export declare function formatEvaluateContextResult(result: EvaluateContextResult): string;
@@ -1,4 +1,4 @@
1
- import { evaluateSignals, getSourceProfile, interpretEvaluations, } from "../core/index.js";
1
+ import { evaluateSignals, getSourceProfile, interpretEvaluations, toReadableContextResult, } from "../core/index.js";
2
2
  const SUPPORTED_INTENTS = [
3
3
  "citation_check",
4
4
  "student_research",
@@ -139,6 +139,8 @@ export function formatEvaluateContextResult(result) {
139
139
  utility_score: item.evaluation.utility.score,
140
140
  confidence: item.evaluation.ranked.confidence,
141
141
  why: item.evaluation.explanation,
142
+ provenance_readiness: item.evaluation.provenance_readiness,
143
+ readable: toReadableContextResult(item.evaluation, item.decision),
142
144
  })),
143
145
  };
144
146
  lines.push("[FRESHCONTEXT_EVALUATION_JSON]", JSON.stringify(structured, null, 2), "[/FRESHCONTEXT_EVALUATION_JSON]");
@@ -0,0 +1 @@
1
+ export { LAMBDA, stampFreshness, toStructuredJSON, formatForLLM, } from "../core/index.js";
@@ -0,0 +1 @@
1
+ export type { FreshContext, ExtractOptions, AdapterResult } from "./core/types.js";