Skip to content

Commit 9a16d46

Browse files
committed
more mcp tools
1 parent d297029 commit 9a16d46

File tree

7 files changed

+1237
-5
lines changed

7 files changed

+1237
-5
lines changed

package.json

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@
594594
{
595595
"name": "elixir-definition",
596596
"displayName": "Elixir Definition Lookup",
597-
"modelDescription": "Find the definition of an Elixir symbol and return its source code",
597+
"modelDescription": "Find and retrieve the source code definition of Elixir/Erlang symbols including modules, functions, types, macros, and callbacks. Returns the actual source code with file location and line numbers for precise code inspection.",
598598
"canBeReferencedInPrompt": true,
599599
"toolReferenceName": "elixir-definition",
600600
"inputSchema": {
@@ -603,7 +603,95 @@
603603
"properties": {
604604
"symbol": {
605605
"type": "string",
606-
"description": "The name of the symbol to find (e.g., 'MyModule.my_function', 'MyModule', ':erlang.term_to_binary')"
606+
"description": "The symbol to find definition for. Supports: modules ('MyModule', 'GenServer'), functions ('MyModule.function', 'String.split/2'), types ('@type my_type'), macros ('defmacro'), Erlang modules (':gen_server'), Erlang functions (':lists.map/2'). Use qualified names (Module.function) for best results."
607+
}
608+
}
609+
}
610+
},
611+
{
612+
"name": "elixir-environment",
613+
"displayName": "Elixir Environment Lookup",
614+
"modelDescription": "Get comprehensive environment information at a specific code location including current module/function context, available aliases, imports, requires, variables in scope with their inferred types, module attributes, implemented behaviours, and all definitions in the file. Essential for understanding code context and available symbols.",
615+
"canBeReferencedInPrompt": true,
616+
"toolReferenceName": "elixir-environment",
617+
"inputSchema": {
618+
"type": "object",
619+
"required": ["location"],
620+
"properties": {
621+
"location": {
622+
"type": "string",
623+
"description": "Location in the code to analyze context. Formats: 'file.ex:line:column', 'file.ex:line', 'lib/my_module.ex:25:10', 'file:///absolute/path/file.ex:10:5'. Use specific line/column for better variable scope and context analysis. Line numbers start at 1."
624+
}
625+
}
626+
}
627+
},
628+
{
629+
"name": "elixir-module-dependencies",
630+
"displayName": "Elixir Module Dependencies",
631+
"modelDescription": "Analyze comprehensive module dependency relationships including direct/reverse dependencies, transitive dependencies, compile-time vs runtime dependencies, imports, aliases, requires, function calls, and struct expansions. Critical for understanding code architecture, impact analysis, and refactoring decisions.",
632+
"canBeReferencedInPrompt": true,
633+
"toolReferenceName": "elixir-module-dependencies",
634+
"inputSchema": {
635+
"type": "object",
636+
"required": ["module"],
637+
"properties": {
638+
"module": {
639+
"type": "string",
640+
"description": "The module name to analyze dependencies for. Supports Elixir modules ('MyApp.MyModule', 'GenServer', 'Enum') and Erlang modules (':gen_server', ':ets'). Returns categorized dependency breakdown showing what the module depends on and what depends on it."
641+
}
642+
}
643+
}
644+
},
645+
{
646+
"name": "elixir-docs",
647+
"displayName": "Elixir Documentation Aggregator",
648+
"modelDescription": "Aggregate and return comprehensive documentation for multiple Elixir modules, functions, types, callbacks, or attributes in a single efficient request. Supports both module-level documentation (with function/type listings) and specific symbol documentation with examples and usage patterns.",
649+
"canBeReferencedInPrompt": true,
650+
"toolReferenceName": "elixir-docs",
651+
"inputSchema": {
652+
"type": "object",
653+
"required": ["modules"],
654+
"properties": {
655+
"modules": {
656+
"type": "array",
657+
"description": "List of symbols to get documentation for. Supports: modules ('Enum', 'GenServer'), functions ('String.split/2', 'Enum.map'), types ('Enum.t/0', 'GenServer.on_start/0'), callbacks ('GenServer.handle_call', 'Application.start'), attributes ('@moduledoc', '@doc'). Mix different symbol types for comprehensive API understanding.",
658+
"items": {
659+
"type": "string"
660+
}
661+
}
662+
}
663+
}
664+
},
665+
{
666+
"name": "elixir-implementation-finder",
667+
"displayName": "Elixir Implementation Finder",
668+
"modelDescription": "Find all concrete implementations of Elixir behaviours, protocols, callbacks, and delegated functions across the entire codebase. Essential for discovering how abstract interfaces are implemented and understanding polymorphic behavior patterns.",
669+
"canBeReferencedInPrompt": true,
670+
"toolReferenceName": "elixir-implementation-finder",
671+
"inputSchema": {
672+
"type": "object",
673+
"required": ["symbol"],
674+
"properties": {
675+
"symbol": {
676+
"type": "string",
677+
"description": "The symbol to find implementations for. Supports: behaviours ('GenServer', 'Application', 'Supervisor'), protocols ('Enumerable', 'Inspect', 'String.Chars'), callbacks ('GenServer.handle_call', 'Application.start'), functions with @impl annotations. Returns file locations and implementation signatures."
678+
}
679+
}
680+
}
681+
},
682+
{
683+
"name": "elixir-types",
684+
"displayName": "Elixir Type Information",
685+
"modelDescription": "Extract comprehensive type information from Elixir modules including @type definitions, @spec annotations, @callback specifications, and Dialyzer inferred contracts. Essential for understanding module interfaces, type safety, function signatures, and behaviour contracts.",
686+
"canBeReferencedInPrompt": true,
687+
"toolReferenceName": "elixir-types",
688+
"inputSchema": {
689+
"type": "object",
690+
"required": ["module"],
691+
"properties": {
692+
"module": {
693+
"type": "string",
694+
"description": "The module name to analyze type information for. Supports Elixir modules ('GenServer', 'MyApp.MyModule', 'Enum') and Erlang modules (':gen_server', ':ets'). Returns detailed type specifications, function signatures, callback definitions, and custom types with their documentation."
607695
}
608696
}
609697
}

src/docs-aggregator-tool.ts

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import * as vscode from "vscode";
2+
import {
3+
ExecuteCommandParams,
4+
ExecuteCommandRequest,
5+
LanguageClient,
6+
} from "vscode-languageclient/node";
7+
8+
interface IParameters {
9+
modules: string[];
10+
}
11+
12+
export class DocsAggregatorTool implements vscode.LanguageModelTool<IParameters> {
13+
constructor(private client: LanguageClient) {}
14+
15+
async prepareInvocation(
16+
options: vscode.LanguageModelToolInvocationPrepareOptions<IParameters>,
17+
_token: vscode.CancellationToken
18+
): Promise<vscode.PreparedToolInvocation> {
19+
return {
20+
invocationMessage: `Getting documentation for: ${options.input.modules.join(", ")}`,
21+
};
22+
}
23+
24+
async invoke(
25+
options: vscode.LanguageModelToolInvocationOptions<IParameters>,
26+
token: vscode.CancellationToken
27+
): Promise<vscode.LanguageModelToolResult> {
28+
const { modules } = options.input;
29+
30+
try {
31+
// Find the llmDocsAggregator command from server capabilities
32+
const command = this.client.initializeResult?.capabilities
33+
.executeCommandProvider?.commands.find((c) =>
34+
c.startsWith("llmDocsAggregator:")
35+
);
36+
37+
if (!command) {
38+
return new vscode.LanguageModelToolResult([
39+
new vscode.LanguageModelTextPart(
40+
"ElixirLS language server is not ready or does not support the llmDocsAggregator command"
41+
),
42+
]);
43+
}
44+
45+
const params: ExecuteCommandParams = {
46+
command: command,
47+
arguments: [modules],
48+
};
49+
50+
const result = await this.client.sendRequest<{
51+
results?: Array<{
52+
// Module documentation fields
53+
module?: string;
54+
moduledoc?: string;
55+
functions?: string[];
56+
macros?: string[];
57+
types?: string[];
58+
callbacks?: string[];
59+
macrocallbacks?: string[];
60+
behaviours?: string[];
61+
62+
// Function/callback/type documentation fields
63+
function?: string;
64+
callback?: string;
65+
type?: string;
66+
arity?: number;
67+
documentation?: string;
68+
69+
// Attribute documentation fields
70+
attribute?: string;
71+
72+
// Error field
73+
error?: string;
74+
}>;
75+
error?: string;
76+
}>(
77+
ExecuteCommandRequest.method,
78+
params,
79+
token
80+
);
81+
82+
if (result?.error) {
83+
return new vscode.LanguageModelToolResult([
84+
new vscode.LanguageModelTextPart(
85+
`Error getting documentation: ${result.error}`
86+
),
87+
]);
88+
}
89+
90+
if (result?.results) {
91+
const parts: vscode.LanguageModelTextPart[] = [];
92+
93+
for (const item of result.results) {
94+
if (item.error) {
95+
const name = item.module || item.function || item.callback || item.type || item.attribute || "Unknown";
96+
parts.push(
97+
new vscode.LanguageModelTextPart(
98+
`## ${name}\nError: ${item.error}\n\n`
99+
)
100+
);
101+
} else if (item.module && item.moduledoc !== undefined) {
102+
// Module documentation
103+
parts.push(
104+
new vscode.LanguageModelTextPart(
105+
`# Module: ${item.module}\n\n`
106+
)
107+
);
108+
109+
if (item.moduledoc) {
110+
parts.push(
111+
new vscode.LanguageModelTextPart(
112+
`${item.moduledoc}\n\n`
113+
)
114+
);
115+
}
116+
117+
if (item.functions && item.functions.length > 0) {
118+
parts.push(
119+
new vscode.LanguageModelTextPart(
120+
`## Functions\n${item.functions.join(", ")}\n\n`
121+
)
122+
);
123+
}
124+
125+
if (item.macros && item.macros.length > 0) {
126+
parts.push(
127+
new vscode.LanguageModelTextPart(
128+
`## Macros\n${item.macros.join(", ")}\n\n`
129+
)
130+
);
131+
}
132+
133+
if (item.types && item.types.length > 0) {
134+
parts.push(
135+
new vscode.LanguageModelTextPart(
136+
`## Types\n${item.types.join(", ")}\n\n`
137+
)
138+
);
139+
}
140+
141+
if (item.callbacks && item.callbacks.length > 0) {
142+
parts.push(
143+
new vscode.LanguageModelTextPart(
144+
`## Callbacks\n${item.callbacks.join(", ")}\n\n`
145+
)
146+
);
147+
}
148+
149+
if (item.macrocallbacks && item.macrocallbacks.length > 0) {
150+
parts.push(
151+
new vscode.LanguageModelTextPart(
152+
`## Macro Callbacks\n${item.macrocallbacks.join(", ")}\n\n`
153+
)
154+
);
155+
}
156+
157+
if (item.behaviours && item.behaviours.length > 0) {
158+
parts.push(
159+
new vscode.LanguageModelTextPart(
160+
`## Behaviours\n${item.behaviours.join(", ")}\n\n`
161+
)
162+
);
163+
}
164+
} else if (item.function) {
165+
// Function documentation
166+
const title = item.arity !== undefined ? `${item.function}/${item.arity}` : item.function;
167+
parts.push(
168+
new vscode.LanguageModelTextPart(
169+
`# Function: ${item.module}.${title}\n\n${item.documentation || "No documentation available"}\n\n`
170+
)
171+
);
172+
} else if (item.callback) {
173+
// Callback documentation
174+
const title = item.arity !== undefined ? `${item.callback}/${item.arity}` : item.callback;
175+
parts.push(
176+
new vscode.LanguageModelTextPart(
177+
`# Callback: ${item.module}.${title}\n\n${item.documentation || "No documentation available"}\n\n`
178+
)
179+
);
180+
} else if (item.type) {
181+
// Type documentation
182+
const title = item.arity !== undefined ? `${item.type}/${item.arity}` : item.type;
183+
parts.push(
184+
new vscode.LanguageModelTextPart(
185+
`# Type: ${item.module}.${title}\n\n${item.documentation || "No documentation available"}\n\n`
186+
)
187+
);
188+
} else if (item.attribute) {
189+
// Attribute documentation
190+
parts.push(
191+
new vscode.LanguageModelTextPart(
192+
`# Attribute: ${item.attribute}\n\n${item.documentation || "No documentation available"}\n\n`
193+
)
194+
);
195+
}
196+
}
197+
198+
return new vscode.LanguageModelToolResult(parts);
199+
}
200+
201+
return new vscode.LanguageModelToolResult([
202+
new vscode.LanguageModelTextPart(
203+
`No documentation found for: ${modules.join(", ")}`
204+
),
205+
]);
206+
} catch (error) {
207+
const errorMessage = error instanceof Error ? error.message : String(error);
208+
return new vscode.LanguageModelToolResult([
209+
new vscode.LanguageModelTextPart(
210+
`Failed to get documentation: ${errorMessage}`
211+
),
212+
]);
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)