@fre4x/grok 1.0.46 → 1.0.49

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 (3) hide show
  1. package/README.md +6 -0
  2. package/dist/index.js +148 -8
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -18,6 +18,8 @@ xAI's Grok is more than an LLM. It's a real-time sensor for the global conscious
18
18
 
19
19
  Search tools support `limit` and `offset` pagination and return normalized results with citations.
20
20
 
21
+ Image generation responses include direct MCP `image` content blocks when media can be embedded. Video responses include direct MCP embedded `resource` blobs because MCP does not define a dedicated `video` content block.
22
+
21
23
  ## Configuration
22
24
 
23
25
  Required environment variable:
@@ -29,6 +31,10 @@ Mock mode is supported for local development and offline validation:
29
31
  MOCK=true npx @fre4x/grok
30
32
  ```
31
33
 
34
+ Optional media embedding controls:
35
+ - `GROK_MAX_EMBEDDED_MEDIA_BYTES`: Maximum bytes to inline into MCP `content` blocks. Defaults to `5242880`.
36
+ - `GROK_MEDIA_FETCH_TIMEOUT_MS`: Timeout for downloading generated media before embedding. Defaults to `15000`.
37
+
32
38
  ## Deploy
33
39
 
34
40
  ```json
package/dist/index.js CHANGED
@@ -18436,7 +18436,8 @@ var require_follow_redirects = __commonJS({
18436
18436
  });
18437
18437
 
18438
18438
  // src/index.ts
18439
- import { pathToFileURL } from "node:url";
18439
+ import { realpathSync } from "node:fs";
18440
+ import { fileURLToPath } from "node:url";
18440
18441
 
18441
18442
  // ../packages/shared/dist/errors.js
18442
18443
  function createApiError(message, statusCode) {
@@ -43634,6 +43635,16 @@ var z3 = external_exports || zod_default || zod_exports;
43634
43635
  var XAI_API_KEY = process.env.XAI_API_KEY;
43635
43636
  var aspectRatioValues = ["1:1", "16:9", "9:16"];
43636
43637
  var MAX_SEARCH_RESULTS = 100;
43638
+ var MAX_EMBEDDED_MEDIA_BYTES = Number.parseInt(
43639
+ process.env.GROK_MAX_EMBEDDED_MEDIA_BYTES || "5242880",
43640
+ 10
43641
+ );
43642
+ var MEDIA_FETCH_TIMEOUT_MS = Number.parseInt(
43643
+ process.env.GROK_MEDIA_FETCH_TIMEOUT_MS || "15000",
43644
+ 10
43645
+ );
43646
+ var MOCK_IMAGE_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
43647
+ var MOCK_VIDEO_BASE64 = "AAAAHGZ0eXBpc29tAAAAAGlzb21pc28yYXZjMQ==";
43637
43648
  var xSearchSchema = paginationSchema.extend({
43638
43649
  query: z3.string().min(1).max(500),
43639
43650
  allowed_handles: z3.array(z3.string().min(1).max(50)).max(10).optional(),
@@ -43984,6 +43995,97 @@ function asArray(value) {
43984
43995
  function asString(value) {
43985
43996
  return typeof value === "string" && value.trim() ? value : void 0;
43986
43997
  }
43998
+ function inferMimeTypeFromUrl(url3, kind) {
43999
+ try {
44000
+ const pathname = new URL(url3).pathname.toLowerCase();
44001
+ if (pathname.endsWith(".png")) return "image/png";
44002
+ if (pathname.endsWith(".jpg") || pathname.endsWith(".jpeg")) {
44003
+ return "image/jpeg";
44004
+ }
44005
+ if (pathname.endsWith(".webp")) return "image/webp";
44006
+ if (pathname.endsWith(".gif")) return "image/gif";
44007
+ if (pathname.endsWith(".mp4")) return "video/mp4";
44008
+ if (pathname.endsWith(".webm")) return "video/webm";
44009
+ if (pathname.endsWith(".mov")) return "video/quicktime";
44010
+ } catch {
44011
+ }
44012
+ return kind === "image" ? "image/png" : "video/mp4";
44013
+ }
44014
+ function normalizeMediaMimeType(value, kind, sourceUrl) {
44015
+ if (typeof value === "string") {
44016
+ const trimmed = value.split(";", 1)[0]?.trim().toLowerCase();
44017
+ if (trimmed && (kind === "image" && trimmed.startsWith("image/") || kind === "video" && trimmed.startsWith("video/"))) {
44018
+ return trimmed;
44019
+ }
44020
+ }
44021
+ return inferMimeTypeFromUrl(sourceUrl, kind) || "application/octet-stream";
44022
+ }
44023
+ async function fetchMediaAsBase64(sourceUrl, kind, client = axios_default) {
44024
+ try {
44025
+ const response = await client.get(sourceUrl, {
44026
+ responseType: "arraybuffer",
44027
+ timeout: MEDIA_FETCH_TIMEOUT_MS,
44028
+ maxContentLength: MAX_EMBEDDED_MEDIA_BYTES,
44029
+ maxBodyLength: MAX_EMBEDDED_MEDIA_BYTES
44030
+ });
44031
+ const buffer = Buffer.from(response.data);
44032
+ if (buffer.byteLength === 0 || buffer.byteLength > MAX_EMBEDDED_MEDIA_BYTES) {
44033
+ return void 0;
44034
+ }
44035
+ return {
44036
+ data: buffer.toString("base64"),
44037
+ mimeType: normalizeMediaMimeType(
44038
+ response.headers["content-type"],
44039
+ kind,
44040
+ sourceUrl
44041
+ )
44042
+ };
44043
+ } catch {
44044
+ return void 0;
44045
+ }
44046
+ }
44047
+ async function buildImageContent(imageUrl, client = axios_default) {
44048
+ const media = await fetchMediaAsBase64(imageUrl, "image", client);
44049
+ if (!media) {
44050
+ return void 0;
44051
+ }
44052
+ return {
44053
+ type: "image",
44054
+ data: media.data,
44055
+ mimeType: media.mimeType
44056
+ };
44057
+ }
44058
+ async function buildVideoContent(videoUrl, client = axios_default) {
44059
+ const media = await fetchMediaAsBase64(videoUrl, "video", client);
44060
+ if (!media) {
44061
+ return void 0;
44062
+ }
44063
+ return {
44064
+ type: "resource",
44065
+ resource: {
44066
+ uri: videoUrl,
44067
+ mimeType: media.mimeType,
44068
+ blob: media.data
44069
+ }
44070
+ };
44071
+ }
44072
+ function buildMockImageContent() {
44073
+ return {
44074
+ type: "image",
44075
+ data: MOCK_IMAGE_BASE64,
44076
+ mimeType: "image/png"
44077
+ };
44078
+ }
44079
+ function buildMockVideoContent(videoUrl) {
44080
+ return {
44081
+ type: "resource",
44082
+ resource: {
44083
+ uri: videoUrl,
44084
+ mimeType: "video/mp4",
44085
+ blob: MOCK_VIDEO_BASE64
44086
+ }
44087
+ };
44088
+ }
43987
44089
  function extractNestedText(value) {
43988
44090
  if (typeof value === "string") {
43989
44091
  return value.trim() ? [value.trim()] : [];
@@ -44215,9 +44317,12 @@ function parseArguments(toolName, schema, args) {
44215
44317
  }
44216
44318
  return { data: parsed.data };
44217
44319
  }
44218
- function toolResult(text, structuredContent) {
44320
+ function toolResult(text, structuredContent, extraContent = []) {
44219
44321
  return {
44220
- content: [{ type: "text", text }],
44322
+ content: [
44323
+ { type: "text", text },
44324
+ ...extraContent
44325
+ ],
44221
44326
  structuredContent
44222
44327
  };
44223
44328
  }
@@ -44370,7 +44475,14 @@ async function handleToolCall(name, args, client = xaiClient) {
44370
44475
  prompt,
44371
44476
  aspect_ratio
44372
44477
  );
44373
- return toolResult(renderImagine(output), output);
44478
+ const mediaContent = IS_MOCK ? [buildMockImageContent()] : (await Promise.all(
44479
+ output.images.map(
44480
+ (image) => buildImageContent(image.url)
44481
+ )
44482
+ )).filter(
44483
+ (item) => item !== void 0
44484
+ );
44485
+ return toolResult(renderImagine(output), output, mediaContent);
44374
44486
  }
44375
44487
  case "grok_animate": {
44376
44488
  const parsed = parseArguments(name, animateSchema, args);
@@ -44390,7 +44502,16 @@ async function handleToolCall(name, args, client = xaiClient) {
44390
44502
  aspect_ratio,
44391
44503
  image_url
44392
44504
  );
44393
- return toolResult(renderVideoGeneration(output), output);
44505
+ const mediaContent = output.video_url === void 0 ? [] : IS_MOCK ? [buildMockVideoContent(output.video_url)] : (await Promise.all([
44506
+ buildVideoContent(output.video_url)
44507
+ ])).filter(
44508
+ (item) => item !== void 0
44509
+ );
44510
+ return toolResult(
44511
+ renderVideoGeneration(output),
44512
+ output,
44513
+ mediaContent
44514
+ );
44394
44515
  }
44395
44516
  case "grok_check_video_status": {
44396
44517
  const parsed = parseArguments(name, videoStatusSchema, args);
@@ -44400,7 +44521,16 @@ async function handleToolCall(name, args, client = xaiClient) {
44400
44521
  const { request_id } = parsed.data;
44401
44522
  const rawResponse = IS_MOCK ? MOCK_FIXTURES.grok_check_video_status : (await client.get(`/videos/status/${request_id}`)).data;
44402
44523
  const output = normalizeVideoStatusResponse(rawResponse);
44403
- return toolResult(renderVideoStatus(output), output);
44524
+ const mediaContent = output.video_url === void 0 ? [] : IS_MOCK ? [buildMockVideoContent(output.video_url)] : (await Promise.all([
44525
+ buildVideoContent(output.video_url)
44526
+ ])).filter(
44527
+ (item) => item !== void 0
44528
+ );
44529
+ return toolResult(
44530
+ renderVideoStatus(output),
44531
+ output,
44532
+ mediaContent
44533
+ );
44404
44534
  }
44405
44535
  default:
44406
44536
  throw new McpError(
@@ -44431,8 +44561,18 @@ async function runServer() {
44431
44561
  await server.connect(transport);
44432
44562
  console.error("Grok MCP Server running on stdio");
44433
44563
  }
44434
- var isMainModule = Boolean(process.argv[1]) && import.meta.url === pathToFileURL(process.argv[1]).href;
44435
- if (isMainModule) {
44564
+ function isMainModule(url3) {
44565
+ if (!process.argv[1]) return false;
44566
+ try {
44567
+ const scriptPath = fileURLToPath(url3);
44568
+ const realScriptPath = realpathSync(scriptPath);
44569
+ const realArgvPath = realpathSync(process.argv[1]);
44570
+ return realScriptPath === realArgvPath;
44571
+ } catch {
44572
+ return false;
44573
+ }
44574
+ }
44575
+ if (isMainModule(import.meta.url)) {
44436
44576
  runServer().catch((error48) => {
44437
44577
  console.error("Fatal error running server:", error48);
44438
44578
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fre4x/grok",
3
- "version": "1.0.46",
3
+ "version": "1.0.49",
4
4
  "description": "An MCP server for xAI (Grok) with search and generation capabilities.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -16,7 +16,7 @@
16
16
  "start": "node dist/index.js",
17
17
  "dev": "tsx src/index.ts",
18
18
  "watch": "tsc -w",
19
- "inspector": "cross-env MOCK=true npx @modelcontextprotocol/inspector node dist/index.js",
19
+ "inspector": "node ../scripts/run-official-inspector.mjs node dist/index.js",
20
20
  "prepublishOnly": "npm run build && npm run typecheck",
21
21
  "test": "vitest run --passWithNoTests --exclude dist"
22
22
  },