@pkwadsy/grok-mcp 1.0.1 → 1.1.1
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/README.md +10 -13
- package/dist/index.js +58 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,21 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
MCP server that wraps the xAI Grok API. Lets Claude and other AI agents delegate thinking, planning, and real-time search to Grok.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Quick Start
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
npm install -g @pkwadsy/grok-mcp
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Or use directly with npx:
|
|
7
|
+
One-liner to add to Claude Code on any device:
|
|
12
8
|
|
|
13
9
|
```bash
|
|
14
|
-
npx @pkwadsy/grok-mcp
|
|
10
|
+
claude mcp add grok -e XAI_API_KEY=your-key -- npx -y @pkwadsy/grok-mcp
|
|
15
11
|
```
|
|
16
12
|
|
|
17
13
|
You need an xAI API key from [console.x.ai](https://console.x.ai).
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
### Alternative: project config
|
|
20
16
|
|
|
21
17
|
Add to your project's `.mcp.json`:
|
|
22
18
|
|
|
@@ -26,7 +22,7 @@ Add to your project's `.mcp.json`:
|
|
|
26
22
|
"grok": {
|
|
27
23
|
"type": "stdio",
|
|
28
24
|
"command": "npx",
|
|
29
|
-
"args": ["@pkwadsy/grok-mcp"],
|
|
25
|
+
"args": ["-y", "@pkwadsy/grok-mcp"],
|
|
30
26
|
"env": {
|
|
31
27
|
"XAI_API_KEY": "your-xai-api-key"
|
|
32
28
|
}
|
|
@@ -44,16 +40,17 @@ Single tool with options for different use cases.
|
|
|
44
40
|
| Parameter | Type | Required | Description |
|
|
45
41
|
|-----------|------|----------|-------------|
|
|
46
42
|
| `prompt` | string | yes | The question or task for Grok |
|
|
43
|
+
| `files` | array | no | Files to include in context (path, optional start_line/end_line) |
|
|
47
44
|
| `system_prompt` | string | no | Custom system prompt |
|
|
48
|
-
| `model` | string | no | Model to use (default: `grok-4.20-
|
|
49
|
-
| `web_search` | boolean | no |
|
|
45
|
+
| `model` | string | no | Model to use (default: `grok-4.20-multi-agent`) |
|
|
46
|
+
| `web_search` | boolean | no | Web search, enabled by default |
|
|
50
47
|
| `x_search` | boolean | no | Enable X/Twitter search |
|
|
51
48
|
|
|
52
49
|
### Available Models
|
|
53
50
|
|
|
54
|
-
- `grok-4.20-
|
|
51
|
+
- `grok-4.20-multi-agent` — multi-agent mode, great for architecture and planning (default)
|
|
52
|
+
- `grok-4.20-reasoning` — flagship reasoning
|
|
55
53
|
- `grok-4.20-non-reasoning` — fast, no reasoning
|
|
56
|
-
- `grok-4.20-multi-agent` — multi-agent mode, great for architecture and planning
|
|
57
54
|
- `grok-4.1-fast-reasoning` — cheaper reasoning
|
|
58
55
|
- `grok-4.1-fast-non-reasoning` — cheapest, fast
|
|
59
56
|
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
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 { readFile } from "node:fs/promises";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
const apiKey = process.env.XAI_API_KEY;
|
|
6
7
|
if (!apiKey) {
|
|
@@ -44,8 +45,16 @@ async function callGrok(options) {
|
|
|
44
45
|
}
|
|
45
46
|
throw new Error("No text content in Grok response");
|
|
46
47
|
}
|
|
47
|
-
server.tool("ask_grok",
|
|
48
|
-
prompt: z.string().describe("The question or task for Grok. Include all relevant context —
|
|
48
|
+
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
|
+
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
|
+
files: z
|
|
51
|
+
.array(z.object({
|
|
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
|
+
}))
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("Files to read and include in the context sent to Grok. Each file is included with its path and line numbers. Use start_line/end_line to include only a specific range"),
|
|
49
58
|
system_prompt: z
|
|
50
59
|
.string()
|
|
51
60
|
.optional()
|
|
@@ -63,12 +72,57 @@ server.tool("ask_grok", "Ask Grok a question. Grok is great for thinking, planni
|
|
|
63
72
|
.boolean()
|
|
64
73
|
.optional()
|
|
65
74
|
.describe("Enable X/Twitter search to find and analyze posts"),
|
|
66
|
-
}, async ({ prompt, system_prompt, model, web_search, x_search }) => {
|
|
75
|
+
}, async ({ prompt, files, system_prompt, model, web_search, x_search }) => {
|
|
67
76
|
const messages = [];
|
|
68
77
|
if (system_prompt) {
|
|
69
78
|
messages.push({ role: "system", content: system_prompt });
|
|
70
79
|
}
|
|
71
|
-
|
|
80
|
+
let userContent = prompt;
|
|
81
|
+
if (files && files.length > 0) {
|
|
82
|
+
const cwd = process.cwd();
|
|
83
|
+
const fileBlocks = [`Working directory: ${cwd}\n`];
|
|
84
|
+
const errors = [];
|
|
85
|
+
for (const file of files) {
|
|
86
|
+
try {
|
|
87
|
+
const raw = await readFile(file.path, "utf-8");
|
|
88
|
+
const allLines = raw.split("\n");
|
|
89
|
+
const totalLines = allLines.length;
|
|
90
|
+
if (file.start_line && file.start_line > totalLines) {
|
|
91
|
+
errors.push(`${file.path}: start_line ${file.start_line} is past end of file (${totalLines} lines)`);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (file.end_line && file.end_line > totalLines) {
|
|
95
|
+
errors.push(`${file.path}: end_line ${file.end_line} is past end of file (${totalLines} lines)`);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (file.start_line && file.end_line && file.start_line > file.end_line) {
|
|
99
|
+
errors.push(`${file.path}: start_line ${file.start_line} is after end_line ${file.end_line}`);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const start = file.start_line ? file.start_line - 1 : 0;
|
|
103
|
+
const end = file.end_line ? file.end_line : totalLines;
|
|
104
|
+
const sliced = allLines.slice(start, end);
|
|
105
|
+
const numbered = sliced
|
|
106
|
+
.map((line, i) => `${start + i + 1}\t${line}`)
|
|
107
|
+
.join("\n");
|
|
108
|
+
const range = file.start_line || file.end_line
|
|
109
|
+
? `:${file.start_line ?? 1}-${file.end_line ?? allLines.length}`
|
|
110
|
+
: "";
|
|
111
|
+
fileBlocks.push(`--- ${file.path}${range} ---\n${numbered}\n---`);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
errors.push(`${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (errors.length > 0) {
|
|
118
|
+
return {
|
|
119
|
+
isError: true,
|
|
120
|
+
content: [{ type: "text", text: `Failed to read ${errors.length} file(s):\n${errors.join("\n")}\n\nFix the paths and try again.` }],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
userContent = `${fileBlocks.join("\n\n")}\n\n${prompt}`;
|
|
124
|
+
}
|
|
125
|
+
messages.push({ role: "user", content: userContent });
|
|
72
126
|
const tools = [];
|
|
73
127
|
if (web_search !== false) {
|
|
74
128
|
tools.push({ type: "web_search" });
|