@everfur/cli 0.1.0 → 0.2.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.
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import { loadHostedMcpConfig, startHostedMcpServer } from './chunk-VHZWTB6J.js';
3
+ import { startRemoteMcpServer, loadRemoteMcpConfig } from './chunk-TBFOL2D3.js';
4
+ import './chunk-WCH6AI6K.js';
5
+ import { CLI_VERSION } from './chunk-CEWWBPPT.js';
6
+
7
+ // src/mcp/container-entry.ts
8
+ var HOSTED_MARKER = "MCP_PUBLIC_URL";
9
+ var SELF_HOSTED_MARKER = "EVERFUR_MCP_HTTP_ACCESS_TOKEN";
10
+ function isSet(env, name) {
11
+ return (env[name] ?? "").trim().length > 0;
12
+ }
13
+ function selectContainerEntry(env) {
14
+ const hosted = isSet(env, HOSTED_MARKER);
15
+ const selfHosted = isSet(env, SELF_HOSTED_MARKER);
16
+ if (hosted && !selfHosted) return { kind: "hosted" };
17
+ if (selfHosted && !hosted) return { kind: "self-hosted" };
18
+ if (hosted && selfHosted) {
19
+ return {
20
+ kind: "refused",
21
+ message: `both ${HOSTED_MARKER} and ${SELF_HOSTED_MARKER} are set. The image runs one entry: the Everfur-hosted server (${HOSTED_MARKER}) or the partner self-hosted server (${SELF_HOSTED_MARKER}), never both.`
22
+ };
23
+ }
24
+ return {
25
+ kind: "refused",
26
+ message: `neither ${HOSTED_MARKER} nor ${SELF_HOSTED_MARKER} is set. Set ${HOSTED_MARKER} (with EVERFUR_API_BASE_URL and EVERFUR_MCP_HTTP_PORT) for the Everfur-hosted server, or ${SELF_HOSTED_MARKER} (with that deployment's Everfur credentials) for the partner self-hosted server.`
27
+ };
28
+ }
29
+
30
+ // src/bin/everfur-mcp-container.ts
31
+ async function main() {
32
+ const choice = selectContainerEntry(process.env);
33
+ if (choice.kind === "refused") throw new Error(choice.message);
34
+ let running;
35
+ if (choice.kind === "hosted") {
36
+ const config = loadHostedMcpConfig(process.env);
37
+ running = await startHostedMcpServer({ config });
38
+ process.stderr.write(`everfur-mcp-hosted listening at ${running.url.toString()} as ${config.publicUrl}
39
+ `);
40
+ } else {
41
+ running = await startRemoteMcpServer(loadRemoteMcpConfig(process.env));
42
+ process.stderr.write(`everfur-mcp-http listening at ${running.url.toString()}
43
+ `);
44
+ }
45
+ const stop = () => {
46
+ running.close().then(() => process.exit(0), () => process.exit(1));
47
+ };
48
+ process.once("SIGTERM", stop);
49
+ process.once("SIGINT", stop);
50
+ }
51
+ if (process.argv.slice(2).includes("--version")) {
52
+ process.stdout.write(`${CLI_VERSION}
53
+ `);
54
+ } else {
55
+ main().catch((error) => {
56
+ process.stderr.write(`everfur-mcp-container: ${error instanceof Error ? error.message : String(error)}
57
+ `);
58
+ process.exit(1);
59
+ });
60
+ }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ import { loadHostedMcpConfig, startHostedMcpServer } from './chunk-VHZWTB6J.js';
3
+ import './chunk-TBFOL2D3.js';
4
+ import './chunk-WCH6AI6K.js';
5
+ import { CLI_VERSION } from './chunk-CEWWBPPT.js';
6
+
7
+ // src/bin/everfur-mcp-hosted.ts
8
+ async function main() {
9
+ const config = loadHostedMcpConfig(process.env);
10
+ const running = await startHostedMcpServer({ config });
11
+ process.stderr.write(`everfur-mcp-hosted listening at ${running.url.toString()} as ${config.publicUrl}
12
+ `);
13
+ const stop = () => {
14
+ running.close().then(() => process.exit(0), () => process.exit(1));
15
+ };
16
+ process.once("SIGTERM", stop);
17
+ process.once("SIGINT", stop);
18
+ }
19
+ if (process.argv.slice(2).includes("--version")) {
20
+ process.stdout.write(`${CLI_VERSION}
21
+ `);
22
+ } else {
23
+ main().catch((error) => {
24
+ process.stderr.write(`everfur-mcp-hosted: ${error instanceof Error ? error.message : String(error)}
25
+ `);
26
+ process.exit(1);
27
+ });
28
+ }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { startRemoteMcpServer, loadRemoteMcpConfig } from './chunk-TBFOL2D3.js';
3
+ import './chunk-WCH6AI6K.js';
4
+ import { CLI_VERSION } from './chunk-CEWWBPPT.js';
5
+
6
+ // src/bin/everfur-mcp-http.ts
7
+ async function main() {
8
+ const running = await startRemoteMcpServer(loadRemoteMcpConfig(process.env));
9
+ process.stderr.write(`everfur-mcp-http listening at ${running.url.toString()}
10
+ `);
11
+ }
12
+ if (process.argv.slice(2).includes("--version")) {
13
+ process.stdout.write(`${CLI_VERSION}
14
+ `);
15
+ } else {
16
+ main().catch((error) => {
17
+ process.stderr.write(`everfur-mcp-http: ${error instanceof Error ? error.message : String(error)}
18
+ `);
19
+ process.exit(1);
20
+ });
21
+ }
@@ -1,369 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { CLI_VERSION, loadBundledDecisionSpace, loadDecisionSpace, nodeFileSystem, detectProject, missingFactsFor, planIntegration, runVerification, systemClock, resolveEndpoint, PARTNER_KEY, createApiClient, fetchTransport } from './chunk-KQZ6ZXL7.js';
3
- import { z } from 'zod';
4
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6
-
7
- var CAPABILITY = z.enum(["chat", "records", "photo", "video", "animations"]);
8
- var IDENTITY_MODE = z.enum(["anonymous", "partner-user-ref", "personalized"]);
9
- function canonicalIdentityMode(mode) {
10
- return mode === "personalized" ? "partner-user-ref" : mode;
11
- }
12
- var MINT_LANGUAGE = z.enum(["node", "python", "go", "ruby", "php", "java", "dotnet"]);
13
- function clientFor(context) {
14
- const endpoint = resolveEndpoint(null, context.env);
15
- if (!endpoint.ok) return { ok: false, errors: endpoint.errors };
16
- return {
17
- ok: true,
18
- client: createApiClient({ transport: fetchTransport, clock: systemClock, endpoint: endpoint.endpoint })
19
- };
20
- }
21
- function supplyOverMcp(fact) {
22
- switch (fact) {
23
- case "decisionTrees":
24
- return "pass `contract_path` (a contract-public projection), or set EVERFUR_CONTRACT_PUBLIC in the server environment";
25
- case "capabilities":
26
- return 'pass `capabilities`, e.g. ["chat"] or ["chat","records"]. Ask the developer which surfaces they want; do not infer it from the project';
27
- case "identityMode":
28
- return 'pass `identity_mode`: "anonymous" (no backend, scoped to the device) or "partner-user-ref" (your backend mints a session). Ask the developer; this changes the integration shape';
29
- case "sessionMintLanguage":
30
- return "pass `session_mint_language`, the language of the backend that will mint sessions";
31
- case "projectType":
32
- case "packageManager":
33
- return "point `project_root` at the application root; this fact is read from the project, not asserted";
34
- default:
35
- return "supply this fact as a tool argument of the same name";
36
- }
37
- }
38
- function refusal(code, message, extra = {}) {
39
- return { isError: true, document: { ok: false, failed_precondition: code, message, ...extra } };
40
- }
41
- var PLAN_INPUT_SHAPE = {
42
- project_root: z.string().describe("Absolute path to the project being integrated."),
43
- capabilities: z.array(CAPABILITY).optional().describe("Which Everfur capabilities this integration uses. Omit if unknown; the tool will refuse and say so."),
44
- identity_mode: IDENTITY_MODE.optional().describe('anonymous = no backend, scoped to the device. partner-user-ref = your backend mints a session against your own user id. "personalized" is accepted as an alias for partner-user-ref.'),
45
- session_mint_language: MINT_LANGUAGE.optional().describe("Backend language that will mint sessions. Only meaningful when identity_mode is partner-user-ref."),
46
- contract_path: z.string().optional().describe("Path to a contract-public projection. Defaults to EVERFUR_CONTRACT_PUBLIC.")
47
- };
48
- function planIntegrationTool(args, context) {
49
- const contractPath = args.contract_path ?? context.env["EVERFUR_CONTRACT_PUBLIC"] ?? null;
50
- const load = contractPath === null ? loadBundledDecisionSpace() : loadDecisionSpace(nodeFileSystem, contractPath);
51
- const input = {
52
- detection: detectProject(nodeFileSystem, args.project_root),
53
- capabilities: args.capabilities ? [...args.capabilities] : null,
54
- identityMode: canonicalIdentityMode(args.identity_mode ?? null),
55
- sessionMintLanguage: args.session_mint_language ?? null,
56
- space: load !== null && load.kind === "loaded" ? load.space : null,
57
- cliVersion: CLI_VERSION
58
- };
59
- const missing = missingFactsFor(input);
60
- if (missing.length > 0) {
61
- return refusal(
62
- "UNDERSPECIFIED_INTEGRATION",
63
- "This plan cannot be produced without the facts below. Ask the developer for each one; do not guess.",
64
- {
65
- missing_facts: missing.map((fact) => ({
66
- fact: fact.fact,
67
- why: fact.reason,
68
- how_to_supply: supplyOverMcp(fact.fact),
69
- // The CLI's own phrasing, kept so the two surfaces are visibly the same refusal and a partner
70
- // moving between them recognises it.
71
- how_to_supply_via_cli: fact.supplyWith
72
- })),
73
- next_call: {
74
- tool: "everfur_plan_integration",
75
- with: "the same arguments plus every missing fact above"
76
- }
77
- }
78
- );
79
- }
80
- return { isError: false, document: { ok: true, plan: planIntegration(input) } };
81
- }
82
- var VERIFY_INPUT_SHAPE = {
83
- user_ref: z.string().optional().describe("End-user id to mint the test session for. Defaults to a CLI-owned value."),
84
- attempts: z.number().int().min(1).max(20).optional().describe("How many times to read the verdict before concluding it is empty.")
85
- };
86
- async function verifyIntegrationTool(args, context) {
87
- const built = clientFor(context);
88
- if (!built.ok) {
89
- return refusal("WIRE_NOT_CONFIGURED", "The API connection could not be configured.", { errors: built.errors });
90
- }
91
- const verification = await runVerification({
92
- client: built.client,
93
- clock: systemClock,
94
- env: context.env,
95
- userRef: args.user_ref ?? "everfur-mcp-verify",
96
- attempts: args.attempts ?? 3,
97
- pollDelayMs: 500
98
- });
99
- return {
100
- // `incomplete` is NOT an error: a SKIP means a credential was absent, which is the agent's cue to supply
101
- // one, not a failure of the integration under test. Only a real FAIL is an error.
102
- isError: verification.status === "failed",
103
- document: {
104
- ok: verification.status === "passed",
105
- status: verification.status,
106
- base_url: built.client.baseUrl,
107
- steps: verification.steps,
108
- // Attribution: a wrongly generated integration has to be traceable to the corpus revision that
109
- // produced it, or a bad recommendation is unfalsifiable after the fact.
110
- attribution: { cli_version: CLI_VERSION, verified_at: (/* @__PURE__ */ new Date()).toISOString() }
111
- }
112
- };
113
- }
114
- var DIAGNOSE_INPUT_SHAPE = {};
115
- async function diagnoseTool(_args, context) {
116
- const built = clientFor(context);
117
- if (!built.ok) {
118
- return refusal("WIRE_NOT_CONFIGURED", "The API connection could not be configured.", { errors: built.errors });
119
- }
120
- const result = await built.client.request({ method: "GET", path: "/widget/v1/diagnostics" });
121
- if (!result.ok && result.kind === "http" && result.status === 404) {
122
- return refusal(
123
- "DIAGNOSTICS_NOT_DEPLOYED",
124
- "This deployment does not serve GET /widget/v1/diagnostics, so the four causes of an empty entitlement verdict cannot be told apart here. This is a gap in the PLATFORM, not in the integration being diagnosed.",
125
- {
126
- prerequisite: "definitive plan #6 (GET /widget/v1/diagnostics), 2 engineer-days, depends on T1.3",
127
- base_url: built.client.baseUrl,
128
- meanwhile: "everfur_verify_integration hard-asserts a non-empty verdict and will still catch the most common failure, an unprovisioned tenant, without naming which of the four causes it is."
129
- }
130
- );
131
- }
132
- if (!result.ok) {
133
- return refusal("DIAGNOSTICS_FAILED", "The diagnostics call did not succeed.", {
134
- kind: result.kind,
135
- ...result.kind === "http" ? { status: result.status, code: result.code } : {},
136
- request_id: result.requestId ?? null
137
- });
138
- }
139
- return { isError: false, document: { ok: true, diagnostics: result.value, base_url: built.client.baseUrl } };
140
- }
141
-
142
- // src/mcp/send-test-message.ts
143
- var DEADLINE_MS = 3e4;
144
- function frameOf(block) {
145
- const line = block.split("\n").find((l) => l.startsWith("data:"));
146
- if (line === void 0) return null;
147
- const raw = line.slice(5).trim();
148
- if (raw.length === 0) return null;
149
- try {
150
- const parsed = JSON.parse(raw);
151
- if (typeof parsed !== "object" || parsed === null) return { kind: "unknown" };
152
- const obj = parsed;
153
- if (typeof obj["token"] === "string") return { kind: "delta", text: obj["token"] };
154
- if (typeof obj["status"] === "string") return { kind: `status:${obj["status"]}` };
155
- if (obj["error"] !== void 0 || typeof obj["code"] === "string") return { kind: "error" };
156
- if (obj["done"] === true) return { kind: "done" };
157
- return { kind: "unknown" };
158
- } catch {
159
- return { kind: "malformed" };
160
- }
161
- }
162
- function uuidFromKey(key) {
163
- const words = [];
164
- for (let lane = 0; lane < 4; lane += 1) {
165
- let hash = 2166136261 ^ lane;
166
- for (let i = 0; i < key.length; i += 1) {
167
- hash ^= key.charCodeAt(i);
168
- hash = hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24)) >>> 0;
169
- }
170
- words.push(hash >>> 0);
171
- }
172
- const hex = words.map((w) => w.toString(16).padStart(8, "0")).join("");
173
- const version = `4${hex.slice(13, 16)}`;
174
- const variant = `${(parseInt(hex[16] ?? "0", 16) & 3 | 8).toString(16)}${hex.slice(17, 20)}`;
175
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${version}-${variant}-${hex.slice(20, 32)}`;
176
- }
177
- function refuse(code, message, extra = {}) {
178
- return { isError: true, document: { ok: false, failed_precondition: code, message, ...extra } };
179
- }
180
- async function sendTestMessage(args, env) {
181
- const endpoint = resolveEndpoint(null, env);
182
- if (!endpoint.ok) {
183
- return refuse("WIRE_NOT_CONFIGURED", "The API connection could not be configured.", {
184
- errors: endpoint.errors
185
- });
186
- }
187
- const base = endpoint.endpoint.baseUrl.replace(/\/$/, "");
188
- const key = (env[PARTNER_KEY.envVar] ?? "").trim();
189
- if (key.length === 0) {
190
- return refuse("NO_PARTNER_KEY", `Set ${PARTNER_KEY.envVar} to the publishable key of the sandbox tenant.`);
191
- }
192
- const sessionId = uuidFromKey(args.idempotency_key);
193
- const headers = {
194
- "x-everfur-partner-key": key,
195
- "x-everfur-session-id": sessionId,
196
- "content-type": "application/json"
197
- };
198
- const verdict = await fetch(`${base}/widget/v1/entitlements`, { headers });
199
- if (!verdict.ok) {
200
- return refuse("ENTITLEMENTS_UNREADABLE", "Could not read the entitlement verdict, so the sandbox status of this tenant is unknown. Refusing rather than guessing.", { status: verdict.status });
201
- }
202
- const body = await verdict.json();
203
- if (body.livemode !== false) {
204
- return refuse(
205
- "NOT_A_SANDBOX_TENANT",
206
- "This tool sends a REAL message and only runs against a sandbox tenant. This key resolves to a tenant reporting livemode true, or to a deployment that does not report livemode at all. Refusing: a test message landing in a tenant that holds real veterinary records is not recoverable by deleting it afterwards.",
207
- {
208
- livemode: body.livemode ?? null,
209
- how_to_fix: "Use the publishable key of a tenant created with is_sandbox true, on a deployment running PARTNER_SANDBOX_MODE_ENABLED."
210
- }
211
- );
212
- }
213
- if (body.capabilities?.["chat.message.create"] === void 0) {
214
- return refuse("CHAT_NOT_GRANTED", "This sandbox tenant does not grant chat.message.create, so a message would be refused by the server.", { granted: Object.keys(body.capabilities ?? {}) });
215
- }
216
- const created = await fetch(`${base}/widget/v1/conversations`, {
217
- method: "POST",
218
- headers,
219
- body: JSON.stringify(args.pet_ref ? { pet_ref: args.pet_ref } : {})
220
- });
221
- if (!created.ok) {
222
- return refuse("CONVERSATION_NOT_CREATED", "Could not open a conversation.", { status: created.status });
223
- }
224
- const conversationId = (await created.json()).conversation_id;
225
- if (conversationId === void 0) {
226
- return refuse("CONVERSATION_NOT_CREATED", "The create call returned no conversation_id.");
227
- }
228
- const controller = new AbortController();
229
- const timer = setTimeout(() => controller.abort(), DEADLINE_MS);
230
- const started = Date.now();
231
- try {
232
- const streamed = await fetch(`${base}/widget/v1/conversations/${encodeURIComponent(conversationId)}/messages`, {
233
- method: "POST",
234
- // `message` is the ONLY field this route accepts. `content` returns a 422 naming body.message,
235
- // which is an hour of a developer's life if the tool gets it wrong.
236
- headers: { ...headers, accept: "text/event-stream", "idempotency-key": args.idempotency_key },
237
- body: JSON.stringify({ message: args.message }),
238
- signal: controller.signal
239
- });
240
- if (!streamed.ok || streamed.body === null) {
241
- return refuse("SEND_FAILED", "The send did not open a stream.", { status: streamed.status });
242
- }
243
- const histogram = {};
244
- let answer = "";
245
- const reader = streamed.body.getReader();
246
- const decoder = new TextDecoder();
247
- let buffer = "";
248
- for (; ; ) {
249
- const { done, value } = await reader.read();
250
- if (done) break;
251
- buffer += decoder.decode(value, { stream: true });
252
- const blocks = buffer.split("\n\n");
253
- buffer = blocks.pop() ?? "";
254
- for (const block of blocks) {
255
- const frame = frameOf(block);
256
- if (frame === null) continue;
257
- histogram[frame.kind] = (histogram[frame.kind] ?? 0) + 1;
258
- if (frame.text !== void 0) answer += frame.text;
259
- }
260
- }
261
- const deltas = histogram["delta"] ?? 0;
262
- return {
263
- // A stream that produced no deltas is a failure even with a 200, because the partner sees an
264
- // empty answer and no error. Reporting it as success is the "green test that proves nothing"
265
- // failure this whole surface exists to avoid.
266
- isError: deltas === 0,
267
- document: {
268
- ok: deltas > 0,
269
- livemode: false,
270
- conversation_id: conversationId,
271
- answer,
272
- frame_histogram: histogram,
273
- elapsed_ms: Date.now() - started,
274
- ...deltas === 0 ? { warning: "The stream carried no delta frames, so no answer text arrived. Check the frame histogram." } : {}
275
- }
276
- };
277
- } catch (error) {
278
- const aborted = error instanceof Error && error.name === "AbortError";
279
- return refuse(
280
- aborted ? "DEADLINE_EXCEEDED" : "STREAM_FAILED",
281
- aborted ? `The turn did not finish within ${DEADLINE_MS / 1e3}s.` : "The stream failed before it completed.",
282
- { detail: error instanceof Error ? error.message : String(error) }
283
- );
284
- } finally {
285
- clearTimeout(timer);
286
- }
287
- }
288
-
289
- // src/mcp/server.ts
290
- var INSTRUCTIONS = `Everfur partner integration tools.
291
-
292
- Everfur is a pet-health API. Partners embed chat and medical-records surfaces in their own apps via
293
- @everfur/sdk (React Native).
294
-
295
- Use everfur_plan_integration FIRST. It refuses underspecified input by naming exactly which facts are
296
- missing. When it refuses, ask the developer those specific questions and call it again. Do not guess the
297
- answers and do not proceed without them: an integration built on a guessed identity mode is wrong in a way
298
- that only shows up in production.
299
-
300
- Then everfur_verify_integration to exercise the real wire, and everfur_diagnose when a working integration
301
- stops returning capabilities.`;
302
- function toContent(result) {
303
- return {
304
- content: [{ type: "text", text: JSON.stringify(result.document, null, 2) }],
305
- isError: result.isError
306
- };
307
- }
308
- function buildServer(context) {
309
- const server = new McpServer(
310
- { name: "everfur", version: CLI_VERSION },
311
- { capabilities: { tools: {} }, instructions: INSTRUCTIONS }
312
- );
313
- server.registerTool(
314
- "everfur_plan_integration",
315
- {
316
- title: "Plan an Everfur integration",
317
- description: "Produce an integration plan for a project. REFUSES underspecified input by naming the missing facts rather than guessing them. Read-only; touches no network and no credential.",
318
- inputSchema: PLAN_INPUT_SHAPE,
319
- annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
320
- },
321
- (args) => toContent(planIntegrationTool(args, context))
322
- );
323
- server.registerTool(
324
- "everfur_verify_integration",
325
- {
326
- title: "Verify the wire end to end",
327
- description: "Mint a session, poll entitlements, and hard-assert the verdict is not empty. Returns PASS/SKIP/FAIL per step with the request id of the call that produced it. Mints a real session, so it is not read-only, but it creates no partner-visible data.",
328
- inputSchema: VERIFY_INPUT_SHAPE,
329
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
330
- },
331
- async (args) => toContent(await verifyIntegrationTool(args, context))
332
- );
333
- server.registerTool(
334
- "everfur_diagnose",
335
- {
336
- title: "Explain why a capability is unavailable",
337
- description: "Separate the four byte-identical causes of an empty entitlement verdict. Read-only. Reports honestly when the deployment does not serve the diagnostics route rather than relaying a bare 404.",
338
- inputSchema: DIAGNOSE_INPUT_SHAPE,
339
- annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
340
- },
341
- async (args) => toContent(await diagnoseTool(args, context))
342
- );
343
- server.registerTool(
344
- "everfur_send_test_message",
345
- {
346
- title: "Send a real message against a sandbox tenant",
347
- description: "Send a REAL message and stream a real veterinary answer, against a SANDBOX tenant only. Refuses unless the entitlement verdict reports livemode false. Returns the answer plus a frame histogram, so a stream that died mid-answer is distinguishable from a short one.",
348
- inputSchema: {
349
- message: z.string().min(1).max(4e3).describe("What to ask. A real pet-health question gets a real answer."),
350
- idempotency_key: z.string().min(8).describe("Required and explicit. The caller owns it, so a retried tool call cannot silently become a second message in the conversation."),
351
- pet_ref: z.string().optional().describe("Optional pet to scope the conversation to. Must already be registered.")
352
- },
353
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
354
- },
355
- async (args) => toContent(await sendTestMessage(args, context.env))
356
- );
357
- return server;
358
- }
359
- async function runStdioServer(env) {
360
- const server = buildServer({ env });
361
- await server.connect(new StdioServerTransport());
362
- }
2
+ import { runStdioServer } from './chunk-WCH6AI6K.js';
3
+ import { CLI_VERSION } from './chunk-CEWWBPPT.js';
363
4
 
364
5
  // src/bin/everfur-mcp.ts
365
- runStdioServer(process.env).catch((error) => {
366
- process.stderr.write(`everfur-mcp: ${error instanceof Error ? error.message : String(error)}
6
+ if (process.argv.slice(2).includes("--version")) {
7
+ process.stdout.write(`${CLI_VERSION}
367
8
  `);
368
- process.exit(1);
369
- });
9
+ } else {
10
+ runStdioServer(process.env).catch((error) => {
11
+ process.stderr.write(`everfur-mcp: ${error instanceof Error ? error.message : String(error)}
12
+ `);
13
+ process.exit(1);
14
+ });
15
+ }
package/dist/everfur.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { ExitCode, runCli } from './chunk-67H43MI6.js';
3
- import { systemClock, fetchTransport, CLI_VERSION, nodeFileSystem } from './chunk-KQZ6ZXL7.js';
2
+ import { runCli } from './chunk-S6UN7D2O.js';
3
+ import { ExitCode, systemClock, fetchTransport, CLI_VERSION, nodeFileSystem, nodeContractCache } from './chunk-CEWWBPPT.js';
4
4
 
5
5
  // src/bin/everfur.ts
6
6
  async function main() {
@@ -12,7 +12,8 @@ async function main() {
12
12
  env: process.env,
13
13
  version: CLI_VERSION,
14
14
  transport: fetchTransport,
15
- clock: systemClock
15
+ clock: systemClock,
16
+ contractCache: nodeContractCache(process.env)
16
17
  }
17
18
  });
18
19
  if (output.stdout.length > 0) process.stdout.write(output.stdout);