@causari/mcp-server 0.1.2 → 0.1.4

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/dist/cli.js CHANGED
@@ -1,28 +1,37 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * Causari MCP Server — local CLI.
4
- *
5
- * Usage:
6
- * causari-mcp # stdio transport (for Claude Code, Cursor)
7
- * npx @causari/mcp-server # same, no install
8
- *
9
- * Configure in Claude Code .mcp.json:
10
- * {
11
- * "mcpServers": {
12
- * "causari": { "command": "causari-mcp" }
13
- * }
14
- * }
15
- */
16
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
- import { createCausariServer } from './server.js';
2
+ import {
3
+ createCausariServer
4
+ } from "./chunk-HQIPIA5N.js";
5
+ import "./chunk-KBU67SEH.js";
6
+
7
+ // src/cli.ts
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ function resolvePack(argv) {
10
+ const flag = argv.indexOf("--pack");
11
+ if (flag >= 0 && argv[flag + 1]) return argv[flag + 1];
12
+ const eq = argv.find((a) => a.startsWith("--pack="));
13
+ if (eq) return eq.slice("--pack=".length);
14
+ return process.env.CAUSARI_PACK || void 0;
15
+ }
18
16
  async function main() {
19
- const server = createCausariServer();
20
- const transport = new StdioServerTransport();
21
- await server.connect(transport);
22
- // Server now runs until stdin closes.
17
+ const pack = resolvePack(process.argv.slice(2));
18
+ let server;
19
+ try {
20
+ server = createCausariServer({ pack });
21
+ if (pack) process.stderr.write(`causari-mcp: loaded pack "${pack}" alongside the core CKG
22
+ `);
23
+ } catch (err) {
24
+ process.stderr.write(
25
+ `causari-mcp: ${err instanceof Error ? err.message : String(err)} \u2014 serving core CKG only
26
+ `
27
+ );
28
+ server = createCausariServer();
29
+ }
30
+ const transport = new StdioServerTransport();
31
+ await server.connect(transport);
23
32
  }
24
33
  main().catch((err) => {
25
- process.stderr.write(`causari-mcp fatal: ${err instanceof Error ? err.stack : String(err)}\n`);
26
- process.exit(1);
34
+ process.stderr.write(`causari-mcp fatal: ${err instanceof Error ? err.stack : String(err)}
35
+ `);
36
+ process.exit(1);
27
37
  });
28
- //# sourceMappingURL=cli.js.map
package/dist/index.d.ts CHANGED
@@ -1,3 +1,247 @@
1
- export { createCausariServer } from './server.js';
2
- export { ALL_TOOLS } from './tools.js';
3
- //# sourceMappingURL=index.d.ts.map
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+
3
+ /**
4
+ * Causal Knowledge Graph — Core Types
5
+ *
6
+ * Schema version 1.0.
7
+ *
8
+ * Three core entities:
9
+ * - CKGEvent: a node on the timeline (something that happened or will happen)
10
+ * - CausalLink: a directed edge between two events with relationship type + confidence
11
+ * - Insight: a derived pattern that links similar causal chains across time
12
+ */
13
+ type Domain = 'technology' | 'humanities' | 'systems' | 'science' | 'economy' | 'geopolitics' | 'philosophy' | 'environment' | 'culture' | 'health' | 'other';
14
+ type Precision = 'millennium' | 'century' | 'decade' | 'year' | 'month' | 'day';
15
+ type CausalRelationship = 'caused' | 'enabled' | 'accelerated' | 'delayed' | 'prevented' | 'inspired';
16
+ type Scope = 'world' | 'personal' | 'org';
17
+ /**
18
+ * Lifecycle status for a live/daily event (e.g. a World Cup fixture).
19
+ *
20
+ * Only meaningful for events that carry the optional live-event fields below.
21
+ * `completed` = it happened; `scheduled` = a future fixture not yet played;
22
+ * `live` = in progress right now; `forecast` = a speculative/projected event.
23
+ * Historical CKG events leave `status` undefined.
24
+ */
25
+ type LiveStatus = 'completed' | 'scheduled' | 'live' | 'forecast';
26
+ type SourceType = 'wikipedia' | 'wikidata' | 'academic' | 'news' | 'user' | 'ai-inferred' | 'demo-seed';
27
+ interface Source {
28
+ type: SourceType;
29
+ url?: string;
30
+ citation?: string;
31
+ /** 0-1 reliability score */
32
+ reliability: number;
33
+ }
34
+ /**
35
+ * A node on the timeline. Can be historical (yearNum < 0 for BCE), present, or future.
36
+ */
37
+ interface CKGEvent {
38
+ /** Stable identifier, kebab-case. e.g. "industrial-revolution" */
39
+ id: string;
40
+ /** Short display name */
41
+ title: string;
42
+ /** Longer description — explains what happened and why it matters */
43
+ description: string;
44
+ /**
45
+ * Numeric year. Negative = BCE. Use Number.POSITIVE_INFINITY for omega (heat death of universe).
46
+ * For sub-year precision, use timestamp field.
47
+ */
48
+ yearNum: number;
49
+ /**
50
+ * Display label for the year. e.g. "1969", "-3500", "70k BCE", "∞"
51
+ */
52
+ yearLabel: string;
53
+ /** Optional Unix epoch in ms for sub-year precision (post-1970 events) */
54
+ timestamp?: number;
55
+ /** Granularity of yearNum — controls UI rendering and query bucketing */
56
+ precision: Precision;
57
+ /** Knowledge domains this event belongs to */
58
+ domains: Domain[];
59
+ /**
60
+ * 0-1, weighted by causal connections + cultural significance.
61
+ * Used to filter for major events when zoomed out or when minImpact is set.
62
+ */
63
+ impactScore: number;
64
+ /** Evidence for this event's existence and properties */
65
+ sources: Source[];
66
+ /** Optional Wikidata Q-id for entity linking */
67
+ wikidataId?: string;
68
+ /** Tier scope — controls visibility */
69
+ scope: Scope;
70
+ /** For org-scoped events: tenant identifier */
71
+ orgId?: string;
72
+ /** For personal-scoped events: user identifier */
73
+ userId?: string;
74
+ /**
75
+ * Free-form tags for search and filtering.
76
+ * Examples: ["AI", "watershed", "infrastructure"]
77
+ */
78
+ tags: string[];
79
+ /**
80
+ * Confidence score for forecasted/predicted events. 0-1.
81
+ * Undefined = certain (historical fact).
82
+ */
83
+ forecastConfidence?: number;
84
+ /** Reasoning trace for forecasted events */
85
+ forecastReasoning?: string;
86
+ /**
87
+ * ISO calendar date (YYYY-MM-DD) — the primary x-axis key for a day-scale
88
+ * timeline. Use alongside `precision: 'day'`. Distinct from `yearNum`, which
89
+ * stays the coarse numeric anchor for the cosmic timeline.
90
+ */
91
+ date?: string;
92
+ /** Human-readable date label, e.g. "June 14, 2026". */
93
+ dateLabel?: string;
94
+ /**
95
+ * Lifecycle status of a live event. Drives card styling on the daily feed.
96
+ * Undefined for historical/static events.
97
+ */
98
+ status?: LiveStatus;
99
+ /**
100
+ * Named real-world entities involved (teams, groups, venues), e.g.
101
+ * ["Mexico", "Group A", "Estadio Azteca"]. Rendered as tag chips.
102
+ */
103
+ entities?: string[];
104
+ /**
105
+ * The headline causal line shown on the card — why this result/fixture
106
+ * matters to the wider tournament. One or two sentences.
107
+ */
108
+ whyItMatters?: string;
109
+ /**
110
+ * Free-text hints about what to watch next (upcoming fixtures, table
111
+ * implications). Rendered as the card footer.
112
+ */
113
+ nextWatchpoints?: string[];
114
+ }
115
+ /**
116
+ * Directed causal edge between two events.
117
+ */
118
+ interface CausalLink {
119
+ id: string;
120
+ /** Cause event id */
121
+ fromEvent: string;
122
+ /** Effect event id */
123
+ toEvent: string;
124
+ relationship: CausalRelationship;
125
+ /** 0-1 confidence in this causal claim */
126
+ confidence: number;
127
+ /**
128
+ * Human-readable explanation of WHY this link exists.
129
+ * e.g. "Steam power required iron-working precision developed in the prior century"
130
+ */
131
+ evidence: string;
132
+ sources: Source[];
133
+ /** Attribution — where this link came from */
134
+ contributedBy: 'system' | 'ai-inferred' | 'community' | 'expert' | string;
135
+ /** Has this link been validated by community/expert review? */
136
+ validated: boolean;
137
+ /** Optional tenant scoping for org links */
138
+ scope: Scope;
139
+ orgId?: string;
140
+ userId?: string;
141
+ }
142
+ /**
143
+ * A derived pattern recognised across multiple causal chains.
144
+ *
145
+ * Example: "Information democratization cycle" — the pattern observed in
146
+ * Gutenberg→Reformation, Internet→Arab Spring, LLMs→? .
147
+ */
148
+ interface Insight {
149
+ id: string;
150
+ /** Short pattern name */
151
+ pattern: string;
152
+ /** Longer description of the pattern */
153
+ description: string;
154
+ /** Causal links that demonstrate this pattern */
155
+ instances: string[];
156
+ /** 0-1 — how useful is this pattern for prediction? */
157
+ predictiveValue: number;
158
+ /** Optional contextual domains where this pattern applies */
159
+ domains: Domain[];
160
+ contributedBy: 'system' | 'ai-inferred' | 'community' | 'expert' | string;
161
+ }
162
+ /**
163
+ * Aggregate snapshot of CKG state — what an MCP query returns when caller asks for graph stats.
164
+ */
165
+ interface CKGStats {
166
+ eventCount: number;
167
+ causalLinkCount: number;
168
+ insightCount: number;
169
+ domains: Record<Domain, number>;
170
+ scope: Scope;
171
+ lastUpdated: string;
172
+ }
173
+
174
+ /**
175
+ * In-memory CKG store. Indexes events and links for fast traversal.
176
+ *
177
+ * For MVP this is in-memory only. Future: back with Cloudflare D1 or Postgres.
178
+ * The Store interface stays the same; only the implementation swaps.
179
+ */
180
+
181
+ declare class CKGStore {
182
+ private events;
183
+ private links;
184
+ private insights;
185
+ private outgoing;
186
+ private incoming;
187
+ constructor(seed?: {
188
+ events?: CKGEvent[];
189
+ causalLinks?: CausalLink[];
190
+ insights?: Insight[];
191
+ });
192
+ addEvent(e: CKGEvent): void;
193
+ addLink(l: CausalLink): void;
194
+ addInsight(i: Insight): void;
195
+ getEvent(id: string): CKGEvent | undefined;
196
+ getLink(id: string): CausalLink | undefined;
197
+ getInsight(id: string): Insight | undefined;
198
+ /**
199
+ * All outgoing causal links from a given event (this event as cause).
200
+ * Filtered by minConfidence if provided.
201
+ */
202
+ outgoingFrom(eventId: string, minConfidence?: number): CausalLink[];
203
+ /**
204
+ * All incoming causal links into a given event (this event as effect).
205
+ */
206
+ incomingTo(eventId: string, minConfidence?: number): CausalLink[];
207
+ allEvents(): CKGEvent[];
208
+ allLinks(): CausalLink[];
209
+ allInsights(): Insight[];
210
+ stats(scope?: Scope): CKGStats;
211
+ }
212
+
213
+ /**
214
+ * Causari MCP Server core.
215
+ *
216
+ * Wires CKGStore + tool handlers to the MCP protocol. Transport-agnostic;
217
+ * cli.ts attaches the stdio transport.
218
+ */
219
+
220
+ declare function createCausariServer(opts?: {
221
+ store?: CKGStore;
222
+ pack?: string;
223
+ }): Server;
224
+
225
+ /**
226
+ * MCP tool definitions for Causari.
227
+ *
228
+ * Each tool wraps a CKG query function and shapes the result for LLM consumption.
229
+ * Output shape priorities:
230
+ * 1. Token-efficient — drop redundant fields
231
+ * 2. Self-explanatory — include relationship + evidence so LLM doesn't need to re-query
232
+ * 3. Calibrated — surface confidence + sources so LLM can communicate uncertainty
233
+ */
234
+
235
+ interface ToolDef {
236
+ name: string;
237
+ description: string;
238
+ inputSchema: {
239
+ type: 'object';
240
+ properties: Record<string, unknown>;
241
+ required?: string[];
242
+ };
243
+ handler: (args: Record<string, unknown>, store: CKGStore) => unknown;
244
+ }
245
+ declare const ALL_TOOLS: ToolDef[];
246
+
247
+ export { ALL_TOOLS, createCausariServer };
package/dist/index.js CHANGED
@@ -1,3 +1,10 @@
1
- export { createCausariServer } from './server.js';
2
- export { ALL_TOOLS } from './tools.js';
3
- //# sourceMappingURL=index.js.map
1
+ import {
2
+ createCausariServer
3
+ } from "./chunk-HQIPIA5N.js";
4
+ import {
5
+ ALL_TOOLS
6
+ } from "./chunk-KBU67SEH.js";
7
+ export {
8
+ ALL_TOOLS,
9
+ createCausariServer
10
+ };
package/dist/smoke.js CHANGED
@@ -1,176 +1,150 @@
1
- /**
2
- * Smoke test — verifies the 5 tool handlers return sensible results
3
- * against the seed CKG data. Run: node dist/smoke.js
4
- */
5
- import { CKGStore, loadSeed } from '@causari/ckg';
6
- import { ALL_TOOLS } from './tools.js';
7
- const store = new CKGStore(loadSeed());
8
- const stats = store.stats();
9
- console.log('CKG stats:', stats);
10
- const tests = [
11
- {
12
- tool: 'query_events',
13
- args: { query: 'printing', limit: 5 },
14
- expect: (r) => r.events?.length > 0 && r.events[0].id === 'printing'
15
- ? null
16
- : `expected printing event, got ${JSON.stringify(r.events?.[0])}`,
17
- },
18
- {
19
- tool: 'query_events',
20
- args: { yearFrom: 1900, yearTo: 2000, minImpact: 0.85 },
21
- expect: (r) => r.events?.every((e) => e.yearNum >= 1900 && e.yearNum <= 2000 && e.impactScore >= 0.85)
22
- ? null
23
- : 'year/impact filter failed',
24
- },
25
- {
26
- tool: 'query_events',
27
- args: { domains: ['technology'], minImpact: 0.9 },
28
- expect: (r) => r.events?.every((e) => e.domains.includes('technology') && e.impactScore >= 0.9)
29
- ? null
30
- : 'domain/impact filter failed',
31
- },
32
- {
33
- tool: 'causal_chain',
34
- args: { event: 'printing', direction: 'effects', depth: 2 },
35
- expect: (r) => {
36
- if (r.error)
37
- return `unexpected error: ${r.error}`;
38
- if (r.root.id !== 'printing')
39
- return `wrong root: ${r.root.id}`;
40
- const titles = r.effects.map((e) => e.id);
41
- // printing rena, printing → indus
42
- if (!titles.includes('rena'))
43
- return `missing rena in effects: ${titles.join(',')}`;
44
- return null;
45
- },
46
- },
47
- {
48
- tool: 'causal_chain',
49
- args: { event: 'transformer', direction: 'causes', depth: 3 },
50
- expect: (r) => {
51
- if (r.error)
52
- return `unexpected error: ${r.error}`;
53
- if (r.root.id !== 'transformer')
54
- return `wrong root: ${r.root.id}`;
55
- const ids = r.causes.map((c) => c.id);
56
- // transformer ← turing_m, dna_disc, iphone (depth 1)
57
- if (!ids.includes('turing_m'))
58
- return `missing turing_m: ${ids.join(',')}`;
59
- return null;
60
- },
61
- },
62
- {
63
- tool: 'causal_chain',
64
- args: { event: 'industrial', direction: 'both', depth: 2 },
65
- expect: (r) => {
66
- if (r.error)
67
- return `unexpected error: ${r.error}`;
68
- if (r.root.id !== 'indus')
69
- return `title fuzzy match should resolve "industrial" → indus, got: ${r.root.id}`;
70
- return null;
71
- },
72
- },
73
- {
74
- tool: 'causal_chain',
75
- args: { event: 'nonexistent_xyz' },
76
- expect: (r) => (r.error ? null : 'expected error for nonexistent event'),
77
- },
78
- {
79
- tool: 'historical_resonance',
80
- args: { situation: 'rapid democratization of knowledge through new technology' },
81
- expect: (r) => {
82
- if (!r.matches || r.matches.length === 0)
83
- return 'expected at least 1 match';
84
- // Should match info-democratization pattern
85
- const found = r.matches.find((m) => m.pattern.toLowerCase().includes('democratization'));
86
- return found ? null : `expected democratization pattern, got: ${r.matches.map((m) => m.pattern).join(', ')}`;
87
- },
88
- },
89
- {
90
- tool: 'predict_scenarios',
91
- args: {
92
- conditions: ['AI capabilities accelerating', 'open-source models improving'],
93
- horizon: 2040,
94
- domains: ['technology'],
95
- },
96
- expect: (r) => {
97
- if (!r.scenarios || r.scenarios.length === 0)
98
- return 'expected at least 1 scenario';
99
- if (!r.scenarios.every((s) => typeof s.probability === 'number'))
100
- return 'scenarios missing probability';
101
- return null;
102
- },
103
- },
104
- {
105
- // Regression: a plausible condition that lexically matches no forecast-event
106
- // text must still fall back to the horizon/domain set, not return empty.
107
- tool: 'predict_scenarios',
108
- args: { conditions: ['AI capability accelerating'], domains: ['technology'] },
109
- expect: (r) => r.scenarios?.length > 0
110
- ? null
111
- : 'expected non-empty scenarios via horizon/domain fallback when conditions do not lexically match',
112
- },
113
- {
114
- tool: 'org_knowledge',
115
- args: { query: 'database decisions', orgId: 'demo-org-no-data' },
116
- expect: (r) => (r.available === false ? null : 'expected available=false for empty org'),
117
- },
118
- // ── AI history extension (events-ai-history.ts) ──────────────────────
119
- {
120
- tool: 'query_events',
121
- args: { query: 'chatgpt', limit: 3 },
122
- expect: (r) => r.events?.some((e) => e.id === 'chatgpt')
123
- ? null
124
- : `expected chatgpt in results, got: ${r.events?.map((e) => e.id).join(',')}`,
125
- },
126
- {
127
- tool: 'causal_chain',
128
- args: { event: 'chatgpt', direction: 'causes', depth: 4 },
129
- expect: (r) => {
130
- if (r.error)
131
- return `unexpected error: ${r.error}`;
132
- const ids = r.causes.map((c) => c.id);
133
- // chatgpt ← gpt3 ← gpt2 ← transformer ← alexnet
134
- if (!ids.includes('gpt3'))
135
- return `missing gpt3: ${ids.join(',')}`;
136
- if (!ids.includes('transformer'))
137
- return `missing transformer: ${ids.join(',')}`;
138
- return null;
139
- },
140
- },
141
- ];
142
- let passed = 0;
143
- let failed = 0;
144
- const failures = [];
145
- for (const t of tests) {
146
- const tool = ALL_TOOLS.find((x) => x.name === t.tool);
147
- if (!tool) {
148
- failures.push(`Unknown tool ${t.tool}`);
149
- failed++;
150
- continue;
1
+ import {
2
+ ALL_TOOLS,
3
+ CKGStore,
4
+ loadSeed
5
+ } from "./chunk-KBU67SEH.js";
6
+
7
+ // src/smoke.ts
8
+ var store = new CKGStore(loadSeed());
9
+ var stats = store.stats();
10
+ console.log("CKG stats:", stats);
11
+ var tests = [
12
+ {
13
+ tool: "query_events",
14
+ args: { query: "printing", limit: 5 },
15
+ expect: (r) => r.events?.length > 0 && r.events[0].id === "printing" ? null : `expected printing event, got ${JSON.stringify(r.events?.[0])}`
16
+ },
17
+ {
18
+ tool: "query_events",
19
+ args: { yearFrom: 1900, yearTo: 2e3, minImpact: 0.85 },
20
+ expect: (r) => r.events?.every((e) => e.yearNum >= 1900 && e.yearNum <= 2e3 && e.impactScore >= 0.85) ? null : "year/impact filter failed"
21
+ },
22
+ {
23
+ tool: "query_events",
24
+ args: { domains: ["technology"], minImpact: 0.9 },
25
+ expect: (r) => r.events?.every((e) => e.domains.includes("technology") && e.impactScore >= 0.9) ? null : "domain/impact filter failed"
26
+ },
27
+ {
28
+ tool: "causal_chain",
29
+ args: { event: "printing", direction: "effects", depth: 2 },
30
+ expect: (r) => {
31
+ if (r.error) return `unexpected error: ${r.error}`;
32
+ if (r.root.id !== "printing") return `wrong root: ${r.root.id}`;
33
+ const titles = r.effects.map((e) => e.id);
34
+ if (!titles.includes("rena")) return `missing rena in effects: ${titles.join(",")}`;
35
+ return null;
36
+ }
37
+ },
38
+ {
39
+ tool: "causal_chain",
40
+ args: { event: "transformer", direction: "causes", depth: 3 },
41
+ expect: (r) => {
42
+ if (r.error) return `unexpected error: ${r.error}`;
43
+ if (r.root.id !== "transformer") return `wrong root: ${r.root.id}`;
44
+ const ids = r.causes.map((c) => c.id);
45
+ if (!ids.includes("turing_m")) return `missing turing_m: ${ids.join(",")}`;
46
+ return null;
151
47
  }
152
- const result = tool.handler(t.args, store);
153
- const err = t.expect(result);
154
- if (err) {
155
- failed++;
156
- failures.push(`[${t.tool}] ${JSON.stringify(t.args)}\n → ${err}`);
48
+ },
49
+ {
50
+ tool: "causal_chain",
51
+ args: { event: "industrial", direction: "both", depth: 2 },
52
+ expect: (r) => {
53
+ if (r.error) return `unexpected error: ${r.error}`;
54
+ if (r.root.id !== "indus") return `title fuzzy match should resolve "industrial" \u2192 indus, got: ${r.root.id}`;
55
+ return null;
157
56
  }
158
- else {
159
- passed++;
57
+ },
58
+ {
59
+ tool: "causal_chain",
60
+ args: { event: "nonexistent_xyz" },
61
+ expect: (r) => r.error ? null : "expected error for nonexistent event"
62
+ },
63
+ {
64
+ tool: "historical_resonance",
65
+ args: { situation: "rapid democratization of knowledge through new technology" },
66
+ expect: (r) => {
67
+ if (!r.matches || r.matches.length === 0) return "expected at least 1 match";
68
+ const found = r.matches.find((m) => m.pattern.toLowerCase().includes("democratization"));
69
+ return found ? null : `expected democratization pattern, got: ${r.matches.map((m) => m.pattern).join(", ")}`;
160
70
  }
71
+ },
72
+ {
73
+ tool: "predict_scenarios",
74
+ args: {
75
+ conditions: ["AI capabilities accelerating", "open-source models improving"],
76
+ horizon: 2040,
77
+ domains: ["technology"]
78
+ },
79
+ expect: (r) => {
80
+ if (!r.scenarios || r.scenarios.length === 0) return "expected at least 1 scenario";
81
+ if (!r.scenarios.every((s) => typeof s.probability === "number")) return "scenarios missing probability";
82
+ return null;
83
+ }
84
+ },
85
+ {
86
+ // Regression: a plausible condition that lexically matches no forecast-event
87
+ // text must still fall back to the horizon/domain set, not return empty.
88
+ tool: "predict_scenarios",
89
+ args: { conditions: ["AI capability accelerating"], domains: ["technology"] },
90
+ expect: (r) => r.scenarios?.length > 0 ? null : "expected non-empty scenarios via horizon/domain fallback when conditions do not lexically match"
91
+ },
92
+ {
93
+ tool: "org_knowledge",
94
+ args: { query: "database decisions", orgId: "demo-org-no-data" },
95
+ expect: (r) => r.available === false ? null : "expected available=false for empty org"
96
+ },
97
+ // ── AI history extension (events-ai-history.ts) ──────────────────────
98
+ {
99
+ tool: "query_events",
100
+ args: { query: "chatgpt", limit: 3 },
101
+ expect: (r) => r.events?.some((e) => e.id === "chatgpt") ? null : `expected chatgpt in results, got: ${r.events?.map((e) => e.id).join(",")}`
102
+ },
103
+ {
104
+ tool: "causal_chain",
105
+ args: { event: "chatgpt", direction: "causes", depth: 4 },
106
+ expect: (r) => {
107
+ if (r.error) return `unexpected error: ${r.error}`;
108
+ const ids = r.causes.map((c) => c.id);
109
+ if (!ids.includes("gpt3")) return `missing gpt3: ${ids.join(",")}`;
110
+ if (!ids.includes("transformer")) return `missing transformer: ${ids.join(",")}`;
111
+ return null;
112
+ }
113
+ }
114
+ ];
115
+ var passed = 0;
116
+ var failed = 0;
117
+ var failures = [];
118
+ for (const t of tests) {
119
+ const tool = ALL_TOOLS.find((x) => x.name === t.tool);
120
+ if (!tool) {
121
+ failures.push(`Unknown tool ${t.tool}`);
122
+ failed++;
123
+ continue;
124
+ }
125
+ const result = tool.handler(t.args, store);
126
+ const err = t.expect(result);
127
+ if (err) {
128
+ failed++;
129
+ failures.push(`[${t.tool}] ${JSON.stringify(t.args)}
130
+ \u2192 ${err}`);
131
+ } else {
132
+ passed++;
133
+ }
161
134
  }
162
- console.log(`\n=== Smoke test ===`);
135
+ console.log(`
136
+ === Smoke test ===`);
163
137
  console.log(`PASS: ${passed}/${tests.length}`);
164
138
  if (failed > 0) {
165
- console.log(`FAIL: ${failed}`);
166
- failures.forEach((f) => console.log(` ${f}`));
167
- process.exit(1);
168
- }
169
- else {
170
- console.log('All tests passed.');
139
+ console.log(`FAIL: ${failed}`);
140
+ failures.forEach((f) => console.log(` ${f}`));
141
+ process.exit(1);
142
+ } else {
143
+ console.log("All tests passed.");
171
144
  }
172
- // Print one full sample so we can eyeball the LLM-facing format
173
- console.log('\n=== Sample causal_chain output ===');
174
- const sample = ALL_TOOLS.find((t) => t.name === 'causal_chain').handler({ event: 'transformer', direction: 'causes', depth: 2 }, store);
145
+ console.log("\n=== Sample causal_chain output ===");
146
+ var sample = ALL_TOOLS.find((t) => t.name === "causal_chain").handler(
147
+ { event: "transformer", direction: "causes", depth: 2 },
148
+ store
149
+ );
175
150
  console.log(JSON.stringify(sample, null, 2));
176
- //# sourceMappingURL=smoke.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@causari/mcp-server",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Causari MCP Server — Causal Knowledge Graph for AI agents",
@@ -19,8 +19,9 @@
19
19
  }
20
20
  },
21
21
  "scripts": {
22
- "build": "tsc",
23
- "dev": "tsc --watch",
22
+ "build": "pnpm --filter @causari/ckg build && tsup",
23
+ "prepublishOnly": "pnpm build",
24
+ "dev": "tsup --watch",
24
25
  "start": "node dist/cli.js",
25
26
  "test": "tsx src/validate.test.ts && tsx src/worker.test.ts && pnpm build && node dist/smoke.js",
26
27
  "test:unit": "tsx src/validate.test.ts && tsx src/worker.test.ts",
@@ -29,10 +30,11 @@
29
30
  "deploy:dry": "wrangler deploy --dry-run"
30
31
  },
31
32
  "dependencies": {
32
- "@modelcontextprotocol/sdk": "^1.12.1",
33
- "@causari/ckg": "workspace:*"
33
+ "@modelcontextprotocol/sdk": "^1.12.1"
34
34
  },
35
35
  "devDependencies": {
36
+ "@causari/ckg": "workspace:*",
37
+ "tsup": "^8.3.0",
36
38
  "wrangler": "^3.99.0",
37
39
  "typescript": "^5.7.0",
38
40
  "tsx": "^4.19.0",