@pkwadsy/grok-mcp 1.1.1 → 1.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.
- package/dist/index.js +43 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import {
|
|
4
|
+
import { globSync, readFileSync } from "node:fs";
|
|
5
|
+
import { resolve } from "node:path";
|
|
5
6
|
import { z } from "zod";
|
|
7
|
+
function parseFileArg(arg) {
|
|
8
|
+
// "path/to/file:10-30" or "path/to/file:10" or "path/to/file" or "src/**/*.ts"
|
|
9
|
+
const match = arg.match(/^(.+?):(\d+)(?:-(\d+))?$/);
|
|
10
|
+
let pattern;
|
|
11
|
+
let startLine;
|
|
12
|
+
let endLine;
|
|
13
|
+
if (match) {
|
|
14
|
+
pattern = match[1];
|
|
15
|
+
startLine = parseInt(match[2], 10);
|
|
16
|
+
endLine = match[3] ? parseInt(match[3], 10) : startLine;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
pattern = arg;
|
|
20
|
+
}
|
|
21
|
+
const resolved = resolve(pattern);
|
|
22
|
+
if (/[*?[\]]/.test(pattern)) {
|
|
23
|
+
const paths = globSync(pattern).sort();
|
|
24
|
+
if (paths.length === 0) {
|
|
25
|
+
return [{ path: pattern }]; // will fail at read time with a clear error
|
|
26
|
+
}
|
|
27
|
+
return paths.map((p) => ({ path: resolve(p), startLine, endLine }));
|
|
28
|
+
}
|
|
29
|
+
return [{ path: resolved, startLine, endLine }];
|
|
30
|
+
}
|
|
6
31
|
const apiKey = process.env.XAI_API_KEY;
|
|
7
32
|
if (!apiKey) {
|
|
8
33
|
console.error("XAI_API_KEY environment variable is required");
|
|
@@ -48,13 +73,9 @@ async function callGrok(options) {
|
|
|
48
73
|
server.tool("ask_grok", `Ask Grok a question. Grok is great for thinking, planning, architecture, and real-time search via web and X/Twitter. Use web_search for current information from the internet. Use x_search to find and analyze posts on X/Twitter. IMPORTANT: Grok has no context about your conversation or codebase. Always include all relevant context directly in the prompt — file contents, error messages, architecture details, constraints, and goals. The more context you provide, the better Grok's response will be. Do not assume Grok knows anything about the current project. Use the files parameter to automatically include file contents with line numbers — this is preferred over pasting code into the prompt. File paths are resolved relative to the server working directory: ${process.cwd()}`, {
|
|
49
74
|
prompt: z.string().describe("The question or task for Grok. Include all relevant context — constraints, background, and goals — since Grok has no access to your conversation or files. Use the files parameter to attach source code rather than pasting it inline"),
|
|
50
75
|
files: z
|
|
51
|
-
.array(z.
|
|
52
|
-
path: z.string().describe("Absolute path to the file"),
|
|
53
|
-
start_line: z.number().optional().describe("First line to include (1-based, inclusive)"),
|
|
54
|
-
end_line: z.number().optional().describe("Last line to include (1-based, inclusive)"),
|
|
55
|
-
}))
|
|
76
|
+
.array(z.string())
|
|
56
77
|
.optional()
|
|
57
|
-
.describe(
|
|
78
|
+
.describe('Files to include in context. Compact syntax: "path/to/file" (whole file), "path/to/file:10-30" (lines 10-30), "path/to/file:10" (just line 10), "src/**/*.ts" (glob pattern). Paths resolve relative to server cwd. All files are sent to Grok with line numbers.'),
|
|
58
79
|
system_prompt: z
|
|
59
80
|
.string()
|
|
60
81
|
.optional()
|
|
@@ -82,36 +103,37 @@ server.tool("ask_grok", `Ask Grok a question. Grok is great for thinking, planni
|
|
|
82
103
|
const cwd = process.cwd();
|
|
83
104
|
const fileBlocks = [`Working directory: ${cwd}\n`];
|
|
84
105
|
const errors = [];
|
|
85
|
-
|
|
106
|
+
const specs = files.flatMap(parseFileArg);
|
|
107
|
+
for (const spec of specs) {
|
|
86
108
|
try {
|
|
87
|
-
const raw =
|
|
109
|
+
const raw = readFileSync(spec.path, "utf-8");
|
|
88
110
|
const allLines = raw.split("\n");
|
|
89
111
|
const totalLines = allLines.length;
|
|
90
|
-
if (
|
|
91
|
-
errors.push(`${
|
|
112
|
+
if (spec.startLine && spec.startLine > totalLines) {
|
|
113
|
+
errors.push(`${spec.path}: line ${spec.startLine} is past end of file (${totalLines} lines)`);
|
|
92
114
|
continue;
|
|
93
115
|
}
|
|
94
|
-
if (
|
|
95
|
-
errors.push(`${
|
|
116
|
+
if (spec.endLine && spec.endLine > totalLines) {
|
|
117
|
+
errors.push(`${spec.path}: line ${spec.endLine} is past end of file (${totalLines} lines)`);
|
|
96
118
|
continue;
|
|
97
119
|
}
|
|
98
|
-
if (
|
|
99
|
-
errors.push(`${
|
|
120
|
+
if (spec.startLine && spec.endLine && spec.startLine > spec.endLine) {
|
|
121
|
+
errors.push(`${spec.path}: start line ${spec.startLine} is after end line ${spec.endLine}`);
|
|
100
122
|
continue;
|
|
101
123
|
}
|
|
102
|
-
const start =
|
|
103
|
-
const end =
|
|
124
|
+
const start = spec.startLine ? spec.startLine - 1 : 0;
|
|
125
|
+
const end = spec.endLine ? spec.endLine : totalLines;
|
|
104
126
|
const sliced = allLines.slice(start, end);
|
|
105
127
|
const numbered = sliced
|
|
106
128
|
.map((line, i) => `${start + i + 1}\t${line}`)
|
|
107
129
|
.join("\n");
|
|
108
|
-
const range =
|
|
109
|
-
? `:${
|
|
130
|
+
const range = spec.startLine || spec.endLine
|
|
131
|
+
? `:${spec.startLine ?? 1}-${spec.endLine ?? totalLines}`
|
|
110
132
|
: "";
|
|
111
|
-
fileBlocks.push(`--- ${
|
|
133
|
+
fileBlocks.push(`--- ${spec.path}${range} ---\n${numbered}\n---`);
|
|
112
134
|
}
|
|
113
135
|
catch (err) {
|
|
114
|
-
errors.push(`${
|
|
136
|
+
errors.push(`${spec.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
115
137
|
}
|
|
116
138
|
}
|
|
117
139
|
if (errors.length > 0) {
|