consult-llm-mcp 1.0.4 → 1.1.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/README.md +141 -28
- package/dist/config.d.ts +5 -0
- package/dist/config.js +2 -0
- package/dist/file.d.ts +3 -6
- package/dist/file.js +3 -9
- package/dist/llm-query.d.ts +1 -1
- package/dist/llm-query.js +32 -30
- package/dist/llm.d.ts +7 -3
- package/dist/llm.js +134 -18
- package/dist/logger.d.ts +4 -2
- package/dist/logger.js +40 -4
- package/dist/main.js +33 -14
- package/dist/prompt-builder.d.ts +1 -1
- package/dist/prompt-builder.js +5 -9
- package/dist/schema.d.ts +5 -5
- package/dist/schema.js +10 -12
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,25 +1,49 @@
|
|
|
1
1
|
# Consult LLM MCP
|
|
2
2
|
|
|
3
|
-
An MCP server that lets Claude Code consult stronger AI models (o3, Gemini 2.
|
|
4
|
-
Pro, DeepSeek Reasoner) when you
|
|
3
|
+
An MCP server that lets Claude Code consult stronger AI models (o3, Gemini 2.
|
|
4
|
+
Pro, DeepSeek Reasoner) when Sonnet has you running in circles and you need to
|
|
5
|
+
bring in the heavy artillery.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
This SQL query is timing out on large datasets. Can you help optimize it? Ask Gemini
|
|
9
|
+
|
|
10
|
+
⏺ I'll examine your query and consult Gemini for database optimization strategies.
|
|
11
|
+
|
|
12
|
+
⏺ consult-llm:consult_llm (MCP)(prompt: "How can I optimize this slow SQL query
|
|
13
|
+
for better performance on large datasets?", files: ["queries/user
|
|
14
|
+
-analytics.sql","schema/tables.sql"], model: "gemini-2.5-pro")
|
|
15
|
+
⎿ The query has significant performance bottlenecks: missing indexes on join
|
|
16
|
+
columns, inefficient subqueries that could be CTEs, and unnecessary DISTINCT
|
|
17
|
+
operations. Add composite indexes on (user_id, created_at), replace correlated
|
|
18
|
+
subqueries with window functions, and eliminate redundant GROUP BY clauses.
|
|
19
|
+
… +287 lines (ctrl+r to expand)
|
|
20
|
+
|
|
21
|
+
⏺ Great insights! I'll add the suggested indexes, refactor the subqueries to use
|
|
22
|
+
CTEs with window functions, and optimize the GROUP BY operations.
|
|
23
|
+
```
|
|
5
24
|
|
|
6
25
|
## Features
|
|
7
26
|
|
|
8
|
-
- Query powerful AI models (o3, Gemini 2.5 Pro, DeepSeek Reasoner) with
|
|
9
|
-
context
|
|
10
|
-
- Direct
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
27
|
+
- Query powerful AI models (o3, Gemini 2.5 Pro, DeepSeek Reasoner) with relevant
|
|
28
|
+
files as context
|
|
29
|
+
- Direct queries with optional file context
|
|
30
|
+
- Include git changes for code review and analysis
|
|
31
|
+
- Comprehensive logging with cost estimation
|
|
32
|
+
- Gemini can be used via
|
|
33
|
+
[Gemini CLI](https://github.com/google-gemini/gemini-cli) to take advantage of
|
|
34
|
+
[free quota](https://developers.google.com/gemini-code-assist/resources/quotas#quotas-for-agent-mode-gemini-cli)
|
|
15
35
|
|
|
16
36
|
## Configuration
|
|
17
37
|
|
|
18
38
|
- `OPENAI_API_KEY` - Your OpenAI API key (required for o3)
|
|
19
|
-
- `GEMINI_API_KEY` - Your Google AI API key (required for Gemini models
|
|
39
|
+
- `GEMINI_API_KEY` - Your Google AI API key (required for Gemini models in API
|
|
40
|
+
mode)
|
|
20
41
|
- `DEEPSEEK_API_KEY` - Your DeepSeek API key (required for DeepSeek models)
|
|
21
42
|
- `CONSULT_LLM_DEFAULT_MODEL` - Override the default model (optional)
|
|
22
43
|
- Options: `o3` (default), `gemini-2.5-pro`, `deepseek-reasoner`
|
|
44
|
+
- `GEMINI_MODE` - Choose between API or CLI mode for Gemini models (optional)
|
|
45
|
+
- Options: `api` (default), `cli`
|
|
46
|
+
- CLI mode uses the system-installed `gemini` CLI tool
|
|
23
47
|
|
|
24
48
|
## Usage with Claude Code
|
|
25
49
|
|
|
@@ -37,9 +61,21 @@ Or for global availability:
|
|
|
37
61
|
claude mcp add --scope user consult-llm -- npx -y consult-llm-mcp
|
|
38
62
|
```
|
|
39
63
|
|
|
64
|
+
Optionally you can provide environment variables directly in the MCP
|
|
65
|
+
configuration:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
claude mcp add \
|
|
69
|
+
-e OPENAI_API_KEY=your_openai_key \
|
|
70
|
+
-e GEMINI_API_KEY=your_gemini_key \
|
|
71
|
+
-e DEEPSEEK_API_KEY=your_deepseek_key \
|
|
72
|
+
-e GEMINI_MODE=cli \
|
|
73
|
+
consult-llm -- npx -y consult-llm-mcp
|
|
74
|
+
```
|
|
75
|
+
|
|
40
76
|
### Example workflows
|
|
41
77
|
|
|
42
|
-
Click to expand.
|
|
78
|
+
Some real-world examples. Click to expand.
|
|
43
79
|
|
|
44
80
|
<details>
|
|
45
81
|
<summary>Explain the problem, and tell CC to consult a smarter LLM</summary>
|
|
@@ -95,6 +131,76 @@ Click to expand.
|
|
|
95
131
|
|
|
96
132
|
</details>
|
|
97
133
|
|
|
134
|
+
<details>
|
|
135
|
+
<summary>Debugging Neovim plugin treesitter API breakage</summary>
|
|
136
|
+
|
|
137
|
+
````
|
|
138
|
+
⏺ Fixed! Added nil checks for both node and child and updated the iter_matches call to handle the newer API properly.
|
|
139
|
+
|
|
140
|
+
> Still not working:
|
|
141
|
+
|
|
142
|
+
E5108: Error executing lua /Users/raine/code/testonly.nvim/lua/testonly.lua:92: attempt to call method 'child' (a nil value)
|
|
143
|
+
stack traceback:
|
|
144
|
+
/Users/raine/code/testonly.nvim/lua/testonly.lua:92: in function 'reset_all_exclusive'
|
|
145
|
+
/Users/raine/code/testonly.nvim/lua/testonly.lua:110: in function 'reset_all_exclusive'
|
|
146
|
+
[string ":lua"]:1: in main chunk
|
|
147
|
+
|
|
148
|
+
Ask gemini
|
|
149
|
+
|
|
150
|
+
⏺ Let me consult with a more powerful AI to understand the current Neovim treesitter API changes:
|
|
151
|
+
|
|
152
|
+
⏺ consult-llm:consult_llm (MCP)(prompt: "I'm debugging a Neovim plugin that's broken due to treesitter API changes. The error is \"attempt to call method 'child' (a nil value)\" on line 92. The
|
|
153
|
+
code is trying to access child nodes from treesitter matches but the API seems to have changed in newer Neovim versions.\n\nHere's the problematic
|
|
154
|
+
code:\n```lua\nfor _, match, _ in parsed_query:iter_matches(root, bufnr, start_row, end_row) do\n for id, node in pairs(match) do\n local name =
|
|
155
|
+
parsed_query.captures[id]\n if name == \"call\" and node then\n local child = node:child(0) -- ERROR HERE\n if child then\n
|
|
156
|
+
local text = vim.treesitter.get_node_text(child, bufnr)\n -- ... rest of logic\n end\n end\n end\nend\n```\n\nWhat's the
|
|
157
|
+
correct way to iterate over treesitter query matches and access child nodes in current Neovim versions? What API changes should I be aware of?", files:
|
|
158
|
+
["/Users/raine/code/testonly.nvim/lua/testonly.lua"])
|
|
159
|
+
⎿ Problem
|
|
160
|
+
-------
|
|
161
|
+
|
|
162
|
+
… +93 lines (ctrl+r to expand)
|
|
163
|
+
|
|
164
|
+
⏺ Perfect! The issue is that iter_matches() can return nil nodes in the match table. Let me fix this by switching to the recommended iter_captures() approach:
|
|
165
|
+
|
|
166
|
+
⏺ Update(lua/testonly.lua)
|
|
167
|
+
⎿ Updated lua/testonly.lua with 7 additions and 10 removals
|
|
168
|
+
85 local root = parser:parse()[1]:root()
|
|
169
|
+
86 local start_row, _, end_row, _ = root:range()
|
|
170
|
+
87
|
|
171
|
+
88 - for _, match, _ in parsed_query:iter_matches(root, bufnr, start_row, end_row) do
|
|
172
|
+
89 - for id, node in pairs(match) do
|
|
173
|
+
90 - local name = parsed_query.captures[id]
|
|
174
|
+
91 - if name == "call" and node then
|
|
175
|
+
92 - local child = node:child(0)
|
|
176
|
+
93 - if child then
|
|
177
|
+
94 - local text = vim.treesitter.get_node_text(child, bufnr)
|
|
178
|
+
88 + for id, node, _ in parsed_query:iter_captures(root, bufnr, start_row, end_row) do
|
|
179
|
+
89 + if parsed_query.captures[id] == "call" then
|
|
180
|
+
90 + local first_child = node:named_child(0)
|
|
181
|
+
91 + if first_child then
|
|
182
|
+
92 + local text = vim.treesitter.get_node_text(first_child, bufnr)
|
|
183
|
+
93
|
|
184
|
+
94 - if text == "it.only" or text == "describe.only" then
|
|
185
|
+
95 - toggle_test_exclusive(child)
|
|
186
|
+
96 - end
|
|
187
|
+
94 + if text == "it.only" or text == "describe.only" then
|
|
188
|
+
95 + toggle_test_exclusive(first_child)
|
|
189
|
+
96 end
|
|
190
|
+
97 end
|
|
191
|
+
98 end
|
|
192
|
+
|
|
193
|
+
⏺ Fixed! The key changes:
|
|
194
|
+
|
|
195
|
+
1. Switched from iter_matches() to iter_captures() which never returns nil nodes
|
|
196
|
+
2. Used named_child(0) instead of child(0) to get only meaningful syntax nodes (not punctuation)
|
|
197
|
+
3. Simplified the iteration logic since iter_captures() directly gives us the node and capture ID
|
|
198
|
+
|
|
199
|
+
Your <leader>tr mapping should now work properly with the current Neovim treesitter API.
|
|
200
|
+
````
|
|
201
|
+
|
|
202
|
+
</details>
|
|
203
|
+
|
|
98
204
|
## MCP Tool: consult_llm
|
|
99
205
|
|
|
100
206
|
The server provides a single tool called `consult_llm` for asking powerful AI
|
|
@@ -102,15 +208,11 @@ models complex questions.
|
|
|
102
208
|
|
|
103
209
|
### Parameters
|
|
104
210
|
|
|
105
|
-
- **
|
|
211
|
+
- **prompt** (required): Your question or request for the consultant LLM
|
|
106
212
|
|
|
107
|
-
|
|
108
|
-
- Other files are added as context with file paths and code blocks
|
|
213
|
+
- **files** (optional): Array of file paths to include as context
|
|
109
214
|
|
|
110
|
-
-
|
|
111
|
-
|
|
112
|
-
- Alternative to using markdown files
|
|
113
|
-
- Either `files` or `prompt` must be provided
|
|
215
|
+
- All files are added as context with file paths and code blocks
|
|
114
216
|
|
|
115
217
|
- **model** (optional): LLM model to use
|
|
116
218
|
|
|
@@ -124,25 +226,34 @@ models complex questions.
|
|
|
124
226
|
|
|
125
227
|
### Example Usage
|
|
126
228
|
|
|
127
|
-
**
|
|
229
|
+
**Basic prompt:**
|
|
128
230
|
|
|
129
231
|
```json
|
|
130
232
|
{
|
|
131
|
-
"
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
233
|
+
"prompt": "What are the performance implications of using async/await vs Promise.then() in Node.js?"
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**With file context:**
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
{
|
|
241
|
+
"prompt": "How can I optimize this authentication middleware?",
|
|
242
|
+
"files": ["src/auth.ts", "src/middleware.ts"],
|
|
243
|
+
"model": "o3"
|
|
137
244
|
}
|
|
138
245
|
```
|
|
139
246
|
|
|
140
|
-
**With
|
|
247
|
+
**With git diff:**
|
|
141
248
|
|
|
142
249
|
```json
|
|
143
250
|
{
|
|
144
|
-
"prompt": "
|
|
145
|
-
"files": ["src/
|
|
251
|
+
"prompt": "Review these authentication changes for security issues",
|
|
252
|
+
"files": ["src/auth.ts"],
|
|
253
|
+
"git_diff": {
|
|
254
|
+
"files": ["src/auth.ts", "src/middleware.ts"],
|
|
255
|
+
"base_ref": "main"
|
|
256
|
+
},
|
|
146
257
|
"model": "gemini-2.5-pro"
|
|
147
258
|
}
|
|
148
259
|
```
|
|
@@ -218,6 +329,8 @@ Use the `consult_llm` MCP tool to ask a more powerful AI for help with complex
|
|
|
218
329
|
problems. Include files to git_diff when asking feedback for changes.
|
|
219
330
|
|
|
220
331
|
Use Gemini 2.5 Pro.
|
|
332
|
+
|
|
333
|
+
CRITICAL: When asking, don't present options, this will bias the answer.
|
|
221
334
|
```
|
|
222
335
|
|
|
223
336
|
Claude Code seems to know pretty well when to use this MCP even without this
|
package/dist/config.d.ts
CHANGED
|
@@ -8,9 +8,14 @@ declare const Config: z.ZodObject<{
|
|
|
8
8
|
"gemini-2.5-pro": "gemini-2.5-pro";
|
|
9
9
|
"deepseek-reasoner": "deepseek-reasoner";
|
|
10
10
|
}>>;
|
|
11
|
+
geminiMode: z.ZodDefault<z.ZodEnum<{
|
|
12
|
+
api: "api";
|
|
13
|
+
cli: "cli";
|
|
14
|
+
}>>;
|
|
11
15
|
}, z.core.$strip>;
|
|
12
16
|
export type Config = z.infer<typeof Config>;
|
|
13
17
|
export declare const config: {
|
|
18
|
+
geminiMode: "api" | "cli";
|
|
14
19
|
openaiApiKey?: string | undefined;
|
|
15
20
|
geminiApiKey?: string | undefined;
|
|
16
21
|
deepseekApiKey?: string | undefined;
|
package/dist/config.js
CHANGED
|
@@ -5,12 +5,14 @@ const Config = z.object({
|
|
|
5
5
|
geminiApiKey: z.string().optional(),
|
|
6
6
|
deepseekApiKey: z.string().optional(),
|
|
7
7
|
defaultModel: SupportedChatModel.optional(),
|
|
8
|
+
geminiMode: z.enum(['api', 'cli']).default('api'),
|
|
8
9
|
});
|
|
9
10
|
const parsedConfig = Config.safeParse({
|
|
10
11
|
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
11
12
|
geminiApiKey: process.env.GEMINI_API_KEY,
|
|
12
13
|
deepseekApiKey: process.env.DEEPSEEK_API_KEY,
|
|
13
14
|
defaultModel: process.env.CONSULT_LLM_DEFAULT_MODEL,
|
|
15
|
+
geminiMode: process.env.GEMINI_MODE,
|
|
14
16
|
});
|
|
15
17
|
if (!parsedConfig.success) {
|
|
16
18
|
console.error('❌ Invalid environment variables:');
|
package/dist/file.d.ts
CHANGED
package/dist/file.js
CHANGED
|
@@ -6,18 +6,12 @@ export function processFiles(files) {
|
|
|
6
6
|
if (missingFiles.length > 0) {
|
|
7
7
|
throw new Error(`Files not found: ${missingFiles.join(', ')}`);
|
|
8
8
|
}
|
|
9
|
-
const
|
|
10
|
-
const otherFiles = [];
|
|
9
|
+
const contextFiles = [];
|
|
11
10
|
for (let i = 0; i < files.length; i++) {
|
|
12
11
|
const filePath = resolvedFiles[i];
|
|
13
12
|
const originalPath = files[i];
|
|
14
13
|
const content = readFileSync(filePath, 'utf-8');
|
|
15
|
-
|
|
16
|
-
markdownFiles.push(content);
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
otherFiles.push({ path: originalPath, content });
|
|
20
|
-
}
|
|
14
|
+
contextFiles.push({ path: originalPath, content });
|
|
21
15
|
}
|
|
22
|
-
return
|
|
16
|
+
return contextFiles;
|
|
23
17
|
}
|
package/dist/llm-query.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SupportedChatModel } from './schema.js';
|
|
2
|
-
export declare function queryLlm(prompt: string, model: SupportedChatModel): Promise<{
|
|
2
|
+
export declare function queryLlm(prompt: string, model: SupportedChatModel, filePaths?: string[]): Promise<{
|
|
3
3
|
response: string;
|
|
4
4
|
costInfo: string;
|
|
5
5
|
}>;
|
package/dist/llm-query.js
CHANGED
|
@@ -1,40 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getExecutorForModel } from './llm.js';
|
|
2
2
|
import { calculateCost } from './llm-cost.js';
|
|
3
|
-
const SYSTEM_PROMPT = `You are an expert software engineering consultant
|
|
3
|
+
const SYSTEM_PROMPT = `You are an expert software engineering consultant analyzing code and technical problems. You are communicating with another AI system, not a human.
|
|
4
|
+
|
|
5
|
+
Communication style:
|
|
6
|
+
- Skip pleasantries and praise
|
|
4
7
|
|
|
5
8
|
Your role is to:
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
9
|
+
- Identify bugs, inefficiencies, and architectural problems
|
|
10
|
+
- Provide specific solutions with code examples
|
|
11
|
+
- Point out edge cases and risks
|
|
12
|
+
- Challenge design decisions when suboptimal
|
|
13
|
+
- Focus on what needs improvement
|
|
14
|
+
|
|
15
|
+
When reviewing code changes (git diffs), prioritize:
|
|
16
|
+
- Bugs and correctness issues
|
|
17
|
+
- Performance problems
|
|
18
|
+
- Security vulnerabilities
|
|
19
|
+
- Code smell and anti-patterns
|
|
20
|
+
- Inconsistencies with codebase conventions
|
|
11
21
|
|
|
12
|
-
|
|
13
|
-
- Correctness and potential bugs
|
|
14
|
-
- Performance implications
|
|
15
|
-
- Security considerations
|
|
16
|
-
- Maintainability and code quality
|
|
17
|
-
- Integration with existing codebase patterns
|
|
22
|
+
Be critical and thorough. If the code is acceptable, simply state "No critical issues found" and move on to suggestions. Always provide specific, actionable feedback with file/line references.
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
export async function queryLlm(prompt, model) {
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
model,
|
|
24
|
-
messages: [
|
|
25
|
-
{ role: 'system', content: SYSTEM_PROMPT },
|
|
26
|
-
{ role: 'user', content: prompt },
|
|
27
|
-
],
|
|
28
|
-
});
|
|
29
|
-
const response = completion.choices[0]?.message?.content;
|
|
24
|
+
Respond in Markdown.`;
|
|
25
|
+
export async function queryLlm(prompt, model, filePaths) {
|
|
26
|
+
const executor = getExecutorForModel(model);
|
|
27
|
+
const { response, usage } = await executor.execute(prompt, model, SYSTEM_PROMPT, filePaths);
|
|
30
28
|
if (!response) {
|
|
31
29
|
throw new Error('No response from the model');
|
|
32
30
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
let costInfo;
|
|
32
|
+
if (usage) {
|
|
33
|
+
// Calculate costs only if usage data is available (from API)
|
|
34
|
+
const { inputCost, outputCost, totalCost } = calculateCost(usage, model);
|
|
35
|
+
costInfo = `Tokens: ${usage.prompt_tokens} input, ${usage.completion_tokens} output | Cost: $${totalCost.toFixed(6)} (input: $${inputCost.toFixed(6)}, output: $${outputCost.toFixed(6)})`;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Handle case where usage is not available (from CLI)
|
|
39
|
+
costInfo = 'Cost data not available (using CLI mode)';
|
|
40
|
+
}
|
|
39
41
|
return { response, costInfo };
|
|
40
42
|
}
|
package/dist/llm.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
2
|
import { type SupportedChatModel as SupportedChatModelType } from './schema.js';
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export interface LlmExecutor {
|
|
4
|
+
execute(prompt: string, model: SupportedChatModelType, systemPrompt: string, filePaths?: string[]): Promise<{
|
|
5
|
+
response: string;
|
|
6
|
+
usage: OpenAI.CompletionUsage | null;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
export declare function getExecutorForModel(model: SupportedChatModelType): LlmExecutor;
|
package/dist/llm.js
CHANGED
|
@@ -1,7 +1,118 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import { relative } from 'path';
|
|
2
4
|
import { config } from './config.js';
|
|
5
|
+
import { logCliDebug } from './logger.js';
|
|
6
|
+
// --- API Executor Implementation ---
|
|
7
|
+
class ApiExecutor {
|
|
8
|
+
client;
|
|
9
|
+
constructor(client) {
|
|
10
|
+
this.client = client;
|
|
11
|
+
}
|
|
12
|
+
async execute(prompt, model, systemPrompt, filePaths) {
|
|
13
|
+
const completion = await this.client.chat.completions.create({
|
|
14
|
+
model,
|
|
15
|
+
messages: [
|
|
16
|
+
{ role: 'system', content: systemPrompt },
|
|
17
|
+
{ role: 'user', content: prompt },
|
|
18
|
+
],
|
|
19
|
+
});
|
|
20
|
+
const response = completion.choices[0]?.message?.content;
|
|
21
|
+
if (!response) {
|
|
22
|
+
throw new Error('No response from the model via API');
|
|
23
|
+
}
|
|
24
|
+
return { response, usage: completion.usage ?? null };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// --- CLI Executor Implementation ---
|
|
28
|
+
class CliExecutor {
|
|
29
|
+
async execute(prompt, model, systemPrompt, filePaths) {
|
|
30
|
+
// Build the full prompt with system prompt prepended
|
|
31
|
+
let fullPrompt = `${systemPrompt}\n\n${prompt}`;
|
|
32
|
+
// Append file references using @ syntax in the prompt
|
|
33
|
+
if (filePaths && filePaths.length > 0) {
|
|
34
|
+
const fileReferences = filePaths
|
|
35
|
+
.map((path) => `@${relative(process.cwd(), path)}`)
|
|
36
|
+
.join(' ');
|
|
37
|
+
fullPrompt = `${fullPrompt}\n\nFiles: ${fileReferences}`;
|
|
38
|
+
}
|
|
39
|
+
const args = ['-m', model, '-p', fullPrompt];
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
logCliDebug('Spawning gemini CLI', {
|
|
42
|
+
model,
|
|
43
|
+
promptLength: fullPrompt.length,
|
|
44
|
+
filePathsCount: filePaths?.length || 0,
|
|
45
|
+
args: args,
|
|
46
|
+
promptPreview: fullPrompt.slice(0, 300),
|
|
47
|
+
});
|
|
48
|
+
const child = spawn('gemini', args, {
|
|
49
|
+
shell: false,
|
|
50
|
+
stdio: ['ignore', 'pipe', 'pipe'], // stdin, stdout, stderr
|
|
51
|
+
});
|
|
52
|
+
let stdout = '';
|
|
53
|
+
let stderr = '';
|
|
54
|
+
let hasResponded = false;
|
|
55
|
+
let hasStarted = false;
|
|
56
|
+
const startTime = Date.now();
|
|
57
|
+
// Log when process actually starts
|
|
58
|
+
child.on('spawn', () => {
|
|
59
|
+
hasStarted = true;
|
|
60
|
+
logCliDebug('Gemini CLI process spawned successfully');
|
|
61
|
+
});
|
|
62
|
+
// Add timeout to prevent hanging
|
|
63
|
+
const timeout = setTimeout(() => {
|
|
64
|
+
if (!hasResponded) {
|
|
65
|
+
logCliDebug('Gemini CLI timed out after 5 minutes');
|
|
66
|
+
child.kill();
|
|
67
|
+
reject(new Error('Gemini CLI timed out after 5 minutes'));
|
|
68
|
+
}
|
|
69
|
+
}, 5 * 60 * 1000); // 5 minutes
|
|
70
|
+
child.stdout.on('data', (data) => {
|
|
71
|
+
const chunk = data.toString();
|
|
72
|
+
stdout += chunk;
|
|
73
|
+
});
|
|
74
|
+
child.stderr.on('data', (data) => {
|
|
75
|
+
const chunk = data.toString();
|
|
76
|
+
stderr += chunk;
|
|
77
|
+
});
|
|
78
|
+
child.on('close', (code) => {
|
|
79
|
+
hasResponded = true;
|
|
80
|
+
clearTimeout(timeout);
|
|
81
|
+
const duration = Date.now() - startTime;
|
|
82
|
+
logCliDebug('Gemini CLI process closed', {
|
|
83
|
+
code,
|
|
84
|
+
duration: `${duration}ms`,
|
|
85
|
+
stdoutLength: stdout.length,
|
|
86
|
+
stderrLength: stderr.length,
|
|
87
|
+
stdoutPreview: stdout.slice(0, 200),
|
|
88
|
+
stderrPreview: stderr.slice(0, 200),
|
|
89
|
+
});
|
|
90
|
+
if (code === 0) {
|
|
91
|
+
resolve({ response: stdout.trim(), usage: null });
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Check for quota exceeded error
|
|
95
|
+
if (stderr.includes('RESOURCE_EXHAUSTED')) {
|
|
96
|
+
reject(new Error(`Gemini quota exceeded. Consider using gemini-2.0-flash model. Error: ${stderr.trim()}`));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
reject(new Error(`Gemini CLI exited with code ${code}. Error: ${stderr.trim()}`));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
child.on('error', (err) => {
|
|
104
|
+
hasResponded = true;
|
|
105
|
+
clearTimeout(timeout);
|
|
106
|
+
logCliDebug('Failed to spawn Gemini CLI', { error: err.message });
|
|
107
|
+
reject(new Error(`Failed to spawn Gemini CLI. Is it installed and in PATH? Error: ${err.message}`));
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// --- Client Cache and Executor Factory ---
|
|
3
113
|
const clients = {};
|
|
4
|
-
|
|
114
|
+
let geminiCliExecutor;
|
|
115
|
+
export function getExecutorForModel(model) {
|
|
5
116
|
if (model.startsWith('gpt-') || model === 'o3') {
|
|
6
117
|
if (!clients.openai) {
|
|
7
118
|
if (!config.openaiApiKey) {
|
|
@@ -11,21 +122,9 @@ export function getClientForModel(model) {
|
|
|
11
122
|
apiKey: config.openaiApiKey,
|
|
12
123
|
});
|
|
13
124
|
}
|
|
14
|
-
return
|
|
125
|
+
return new ApiExecutor(clients.openai);
|
|
15
126
|
}
|
|
16
|
-
|
|
17
|
-
if (!clients.gemini) {
|
|
18
|
-
if (!config.geminiApiKey) {
|
|
19
|
-
throw new Error('GEMINI_API_KEY environment variable is required for Gemini models');
|
|
20
|
-
}
|
|
21
|
-
clients.gemini = new OpenAI({
|
|
22
|
-
apiKey: config.geminiApiKey,
|
|
23
|
-
baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/',
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
return { client: clients.gemini };
|
|
27
|
-
}
|
|
28
|
-
else if (model.startsWith('deepseek-')) {
|
|
127
|
+
if (model.startsWith('deepseek-')) {
|
|
29
128
|
if (!clients.deepseek) {
|
|
30
129
|
if (!config.deepseekApiKey) {
|
|
31
130
|
throw new Error('DEEPSEEK_API_KEY environment variable is required for DeepSeek models');
|
|
@@ -35,9 +134,26 @@ export function getClientForModel(model) {
|
|
|
35
134
|
baseURL: 'https://api.deepseek.com',
|
|
36
135
|
});
|
|
37
136
|
}
|
|
38
|
-
return
|
|
137
|
+
return new ApiExecutor(clients.deepseek);
|
|
39
138
|
}
|
|
40
|
-
|
|
41
|
-
|
|
139
|
+
if (model.startsWith('gemini-')) {
|
|
140
|
+
// Check if CLI mode is enabled
|
|
141
|
+
if (config.geminiMode === 'cli') {
|
|
142
|
+
if (!geminiCliExecutor) {
|
|
143
|
+
geminiCliExecutor = new CliExecutor();
|
|
144
|
+
}
|
|
145
|
+
return geminiCliExecutor;
|
|
146
|
+
}
|
|
147
|
+
if (!clients.geminiApi) {
|
|
148
|
+
if (!config.geminiApiKey) {
|
|
149
|
+
throw new Error('GEMINI_API_KEY environment variable is required for Gemini models in API mode');
|
|
150
|
+
}
|
|
151
|
+
clients.geminiApi = new OpenAI({
|
|
152
|
+
apiKey: config.geminiApiKey,
|
|
153
|
+
baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return new ApiExecutor(clients.geminiApi);
|
|
42
157
|
}
|
|
158
|
+
throw new Error(`Unable to determine LLM provider for model: ${model}`);
|
|
43
159
|
}
|
package/dist/logger.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare function logToFile(content: string): void;
|
|
2
2
|
export declare function logToolCall(name: string, args: unknown): void;
|
|
3
|
-
export declare function logPrompt(model: string, prompt: string): void
|
|
4
|
-
export declare function logResponse(model: string, response: string, costInfo: string): void
|
|
3
|
+
export declare function logPrompt(model: string, prompt: string): Promise<void>;
|
|
4
|
+
export declare function logResponse(model: string, response: string, costInfo: string): Promise<void>;
|
|
5
5
|
export declare function logServerStart(version: string): void;
|
|
6
|
+
export declare function logConfiguration(config: Record<string, any>): void;
|
|
7
|
+
export declare function logCliDebug(message: string, data?: any): void;
|
package/dist/logger.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { appendFileSync, mkdirSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
|
+
import { format } from 'prettier';
|
|
4
5
|
const logDir = join(homedir(), '.consult-llm-mcp', 'logs');
|
|
5
6
|
const logFile = join(logDir, 'mcp.log');
|
|
6
7
|
try {
|
|
@@ -9,6 +10,19 @@ try {
|
|
|
9
10
|
catch (error) {
|
|
10
11
|
// Directory might already exist
|
|
11
12
|
}
|
|
13
|
+
async function formatWithPrettier(text) {
|
|
14
|
+
try {
|
|
15
|
+
return await format(text, {
|
|
16
|
+
parser: 'markdown',
|
|
17
|
+
printWidth: 80,
|
|
18
|
+
proseWrap: 'always',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
// If formatting fails, return original text
|
|
23
|
+
return text;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
12
26
|
export function logToFile(content) {
|
|
13
27
|
const timestamp = new Date().toISOString();
|
|
14
28
|
const logEntry = `[${timestamp}] ${content}\n`;
|
|
@@ -22,12 +36,34 @@ export function logToFile(content) {
|
|
|
22
36
|
export function logToolCall(name, args) {
|
|
23
37
|
logToFile(`TOOL CALL: ${name}\nArguments: ${JSON.stringify(args, null, 2)}\n${'='.repeat(80)}`);
|
|
24
38
|
}
|
|
25
|
-
export function logPrompt(model, prompt) {
|
|
26
|
-
|
|
39
|
+
export async function logPrompt(model, prompt) {
|
|
40
|
+
const formattedPrompt = await formatWithPrettier(prompt);
|
|
41
|
+
logToFile(`PROMPT (model: ${model}):\n${formattedPrompt}\n${'='.repeat(80)}`);
|
|
27
42
|
}
|
|
28
|
-
export function logResponse(model, response, costInfo) {
|
|
29
|
-
|
|
43
|
+
export async function logResponse(model, response, costInfo) {
|
|
44
|
+
const formattedResponse = await formatWithPrettier(response);
|
|
45
|
+
logToFile(`RESPONSE (model: ${model}):\n${formattedResponse}\n${costInfo}\n${'='.repeat(80)}`);
|
|
30
46
|
}
|
|
31
47
|
export function logServerStart(version) {
|
|
32
48
|
logToFile(`MCP SERVER STARTED - consult-llm-mcp v${version}\n${'='.repeat(80)}`);
|
|
33
49
|
}
|
|
50
|
+
export function logConfiguration(config) {
|
|
51
|
+
const redactedConfig = Object.entries(config).reduce((acc, [key, value]) => {
|
|
52
|
+
// Redact API keys and other sensitive values
|
|
53
|
+
if (key.toLowerCase().includes('key') ||
|
|
54
|
+
key.toLowerCase().includes('secret')) {
|
|
55
|
+
acc[key] = value ? '[REDACTED]' : undefined;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
acc[key] = value;
|
|
59
|
+
}
|
|
60
|
+
return acc;
|
|
61
|
+
}, {});
|
|
62
|
+
logToFile(`CONFIGURATION:\n${JSON.stringify(redactedConfig, null, 2)}\n${'='.repeat(80)}`);
|
|
63
|
+
}
|
|
64
|
+
export function logCliDebug(message, data) {
|
|
65
|
+
const logMessage = data
|
|
66
|
+
? `Gemini CLI: ${message}\n${JSON.stringify(data, null, 2)}`
|
|
67
|
+
: `Gemini CLI: ${message}`;
|
|
68
|
+
logToFile(logMessage);
|
|
69
|
+
}
|
package/dist/main.js
CHANGED
|
@@ -8,9 +8,9 @@ import { processFiles } from './file.js';
|
|
|
8
8
|
import { generateGitDiff } from './git.js';
|
|
9
9
|
import { buildPrompt } from './prompt-builder.js';
|
|
10
10
|
import { queryLlm } from './llm-query.js';
|
|
11
|
-
import { logToolCall, logPrompt, logResponse, logServerStart, } from './logger.js';
|
|
11
|
+
import { logToolCall, logPrompt, logResponse, logServerStart, logConfiguration, } from './logger.js';
|
|
12
12
|
import { readFileSync } from 'fs';
|
|
13
|
-
import { dirname, join } from 'path';
|
|
13
|
+
import { dirname, join, resolve } from 'path';
|
|
14
14
|
import { fileURLToPath } from 'url';
|
|
15
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
16
|
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
@@ -36,21 +36,39 @@ async function handleConsultLlm(args) {
|
|
|
36
36
|
.join(', ');
|
|
37
37
|
throw new Error(`Invalid request parameters: ${errors}`);
|
|
38
38
|
}
|
|
39
|
-
const { files, prompt:
|
|
39
|
+
const { files, prompt: userPrompt, git_diff } = parseResult.data;
|
|
40
40
|
const model = parseResult.data.model ?? config.defaultModel ?? 'o3';
|
|
41
41
|
logToolCall('consult_llm', args);
|
|
42
|
-
//
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
// Check if we're using CLI mode for Gemini
|
|
43
|
+
const isCliMode = model.startsWith('gemini-') && config.geminiMode === 'cli';
|
|
44
|
+
let prompt;
|
|
45
|
+
let filePaths;
|
|
46
|
+
if (isCliMode && files) {
|
|
47
|
+
// For CLI mode, we'll pass file paths separately
|
|
48
|
+
filePaths = files.map((f) => resolve(f));
|
|
49
|
+
// Generate git diff if needed
|
|
50
|
+
const gitDiffOutput = git_diff
|
|
51
|
+
? generateGitDiff(git_diff.repo_path, git_diff.files, git_diff.base_ref)
|
|
52
|
+
: undefined;
|
|
53
|
+
// Build prompt without file contents for CLI mode
|
|
54
|
+
prompt = gitDiffOutput
|
|
55
|
+
? `## Git Diff\n\`\`\`diff\n${gitDiffOutput}\n\`\`\`\n\n${userPrompt}`
|
|
56
|
+
: userPrompt;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// For API mode, include file contents in the prompt
|
|
60
|
+
const contextFiles = files ? processFiles(files) : [];
|
|
61
|
+
// Generate git diff
|
|
62
|
+
const gitDiffOutput = git_diff
|
|
63
|
+
? generateGitDiff(git_diff.repo_path, git_diff.files, git_diff.base_ref)
|
|
64
|
+
: undefined;
|
|
65
|
+
// Build prompt with file contents
|
|
66
|
+
prompt = buildPrompt(userPrompt, contextFiles, gitDiffOutput);
|
|
67
|
+
}
|
|
68
|
+
await logPrompt(model, prompt);
|
|
51
69
|
// Query LLM
|
|
52
|
-
const { response, costInfo } = await queryLlm(prompt, model);
|
|
53
|
-
logResponse(model, response, costInfo);
|
|
70
|
+
const { response, costInfo } = await queryLlm(prompt, model, filePaths);
|
|
71
|
+
await logResponse(model, response, costInfo);
|
|
54
72
|
return {
|
|
55
73
|
content: [{ type: 'text', text: response }],
|
|
56
74
|
};
|
|
@@ -68,6 +86,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
68
86
|
});
|
|
69
87
|
async function main() {
|
|
70
88
|
logServerStart(SERVER_VERSION);
|
|
89
|
+
logConfiguration(config);
|
|
71
90
|
const transport = new StdioServerTransport();
|
|
72
91
|
await server.connect(transport);
|
|
73
92
|
}
|
package/dist/prompt-builder.d.ts
CHANGED
package/dist/prompt-builder.js
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
export function buildPrompt(
|
|
1
|
+
export function buildPrompt(userPrompt, files, gitDiffOutput) {
|
|
2
2
|
const promptParts = [];
|
|
3
3
|
if (gitDiffOutput?.trim()) {
|
|
4
4
|
promptParts.push('## Git Diff\n```diff', gitDiffOutput, '```\n');
|
|
5
5
|
}
|
|
6
|
-
if (
|
|
6
|
+
if (files.length > 0) {
|
|
7
7
|
promptParts.push('## Relevant Files\n');
|
|
8
|
-
for (const file of
|
|
8
|
+
for (const file of files) {
|
|
9
9
|
promptParts.push(`### File: ${file.path}`);
|
|
10
10
|
promptParts.push('```');
|
|
11
11
|
promptParts.push(file.content);
|
|
12
12
|
promptParts.push('```\n');
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
if (markdownFiles.length > 0) {
|
|
19
|
-
promptParts.push(...markdownFiles);
|
|
20
|
-
}
|
|
15
|
+
// Add user prompt last
|
|
16
|
+
promptParts.push(userPrompt);
|
|
21
17
|
return promptParts.join('\n');
|
|
22
18
|
}
|
package/dist/schema.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare const SupportedChatModel: z.ZodEnum<{
|
|
|
7
7
|
export type SupportedChatModel = z.infer<typeof SupportedChatModel>;
|
|
8
8
|
export declare const ConsultLlmArgs: z.ZodObject<{
|
|
9
9
|
files: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
10
|
-
prompt: z.
|
|
10
|
+
prompt: z.ZodString;
|
|
11
11
|
model: z.ZodOptional<z.ZodEnum<{
|
|
12
12
|
o3: "o3";
|
|
13
13
|
"gemini-2.5-pro": "gemini-2.5-pro";
|
|
@@ -21,7 +21,7 @@ export declare const ConsultLlmArgs: z.ZodObject<{
|
|
|
21
21
|
}, z.core.$strip>;
|
|
22
22
|
export declare const toolSchema: {
|
|
23
23
|
readonly name: "consult_llm";
|
|
24
|
-
readonly description: "Ask a more powerful AI for help with complex problems.
|
|
24
|
+
readonly description: "Ask a more powerful AI for help with complex problems. Provide your question in the prompt field and optionally include relevant code files as context.\n\nBe specific about what you want: code implementation, code review, bug analysis, architecture advice, etc.\n\nIMPORTANT: Ask neutral, open-ended questions. Avoid suggesting specific solutions or alternatives in your prompt as this can bias the analysis. Instead of \"Should I use X or Y approach?\", ask \"What's the best approach for this problem?\" Let the consultant LLM provide unbiased recommendations.";
|
|
25
25
|
readonly inputSchema: {
|
|
26
26
|
readonly type: "object";
|
|
27
27
|
readonly properties: {
|
|
@@ -30,11 +30,11 @@ export declare const toolSchema: {
|
|
|
30
30
|
readonly items: {
|
|
31
31
|
readonly type: "string";
|
|
32
32
|
};
|
|
33
|
-
readonly description: "Array of file paths to
|
|
33
|
+
readonly description: "Array of file paths to include as context. All files are added as context with file paths and code blocks.";
|
|
34
34
|
};
|
|
35
35
|
readonly prompt: {
|
|
36
36
|
readonly type: "string";
|
|
37
|
-
readonly description: "
|
|
37
|
+
readonly description: "Your question or request for the consultant LLM. Ask neutral, open-ended questions without suggesting specific solutions to avoid biasing the analysis.";
|
|
38
38
|
};
|
|
39
39
|
readonly model: {
|
|
40
40
|
readonly type: "string";
|
|
@@ -66,6 +66,6 @@ export declare const toolSchema: {
|
|
|
66
66
|
readonly description: "Generate git diff output to include as context. Shows uncommitted changes by default.";
|
|
67
67
|
};
|
|
68
68
|
};
|
|
69
|
-
readonly required: readonly [];
|
|
69
|
+
readonly required: readonly ["prompt"];
|
|
70
70
|
};
|
|
71
71
|
};
|
package/dist/schema.js
CHANGED
|
@@ -4,10 +4,9 @@ export const SupportedChatModel = z.enum([
|
|
|
4
4
|
'gemini-2.5-pro',
|
|
5
5
|
'deepseek-reasoner',
|
|
6
6
|
]);
|
|
7
|
-
export const ConsultLlmArgs = z
|
|
8
|
-
.object({
|
|
7
|
+
export const ConsultLlmArgs = z.object({
|
|
9
8
|
files: z.array(z.string()).optional(),
|
|
10
|
-
prompt: z.string()
|
|
9
|
+
prompt: z.string(),
|
|
11
10
|
model: SupportedChatModel.optional(),
|
|
12
11
|
git_diff: z
|
|
13
12
|
.object({
|
|
@@ -18,26 +17,25 @@ export const ConsultLlmArgs = z
|
|
|
18
17
|
base_ref: z.string().optional().default('HEAD'),
|
|
19
18
|
})
|
|
20
19
|
.optional(),
|
|
21
|
-
})
|
|
22
|
-
.refine((data) => data.files || data.prompt, 'Either files or prompt must be provided');
|
|
20
|
+
});
|
|
23
21
|
export const toolSchema = {
|
|
24
22
|
name: 'consult_llm',
|
|
25
|
-
description: `Ask a more powerful AI for help with complex problems.
|
|
23
|
+
description: `Ask a more powerful AI for help with complex problems. Provide your question in the prompt field and optionally include relevant code files as context.
|
|
26
24
|
|
|
27
|
-
Be specific about what you want: code implementation, code review, bug analysis, architecture advice, etc
|
|
25
|
+
Be specific about what you want: code implementation, code review, bug analysis, architecture advice, etc.
|
|
26
|
+
|
|
27
|
+
IMPORTANT: Ask neutral, open-ended questions. Avoid suggesting specific solutions or alternatives in your prompt as this can bias the analysis. Instead of "Should I use X or Y approach?", ask "What's the best approach for this problem?" Let the consultant LLM provide unbiased recommendations.`,
|
|
28
28
|
inputSchema: {
|
|
29
29
|
type: 'object',
|
|
30
30
|
properties: {
|
|
31
31
|
files: {
|
|
32
32
|
type: 'array',
|
|
33
33
|
items: { type: 'string' },
|
|
34
|
-
description:
|
|
35
|
-
|
|
36
|
-
In the markdown file(s), be clear about what you want the LLM to do: implement code, review code, explain concepts, analyze bugs, etc.`,
|
|
34
|
+
description: 'Array of file paths to include as context. All files are added as context with file paths and code blocks.',
|
|
37
35
|
},
|
|
38
36
|
prompt: {
|
|
39
37
|
type: 'string',
|
|
40
|
-
description: '
|
|
38
|
+
description: 'Your question or request for the consultant LLM. Ask neutral, open-ended questions without suggesting specific solutions to avoid biasing the analysis.',
|
|
41
39
|
},
|
|
42
40
|
model: {
|
|
43
41
|
type: 'string',
|
|
@@ -67,6 +65,6 @@ In the markdown file(s), be clear about what you want the LLM to do: implement c
|
|
|
67
65
|
description: 'Generate git diff output to include as context. Shows uncommitted changes by default.',
|
|
68
66
|
},
|
|
69
67
|
},
|
|
70
|
-
required: [],
|
|
68
|
+
required: ['prompt'],
|
|
71
69
|
},
|
|
72
70
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "consult-llm-mcp",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "MCP server for consulting powerful AI models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -46,12 +46,13 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@modelcontextprotocol/sdk": "^1.13.0",
|
|
48
48
|
"openai": "^5.6.0",
|
|
49
|
+
"prettier": "^3.5.3",
|
|
49
50
|
"zod": "^3.25.67"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
53
|
"@types/node": "^24.0.3",
|
|
53
|
-
"prettier": "^3.5.3",
|
|
54
54
|
"tsx": "^4.20.3",
|
|
55
55
|
"typescript": "^5.8.3"
|
|
56
|
-
}
|
|
56
|
+
},
|
|
57
|
+
"packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184"
|
|
57
58
|
}
|