@mkrlbs/mcp-adonisjs 0.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 +139 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +231 -0
- package/build/index.js.map +1 -0
- package/package.json +36 -0
- package/src/index.ts +264 -0
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# AdonisJS MCP Server
|
|
2
|
+
|
|
3
|
+
A secure [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server for executing AdonisJS Ace commands. This server allows AI assistants to interact with AdonisJS projects through a controlled, secure interface.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔒 **Secure by default**: Blacklists dangerous commands and prevents shell injection
|
|
8
|
+
- 🛠️ **Multiple tools**: Pre-configured tools for common AdonisJS operations
|
|
9
|
+
- ✅ **Validation**: Strict argument validation using Zod schemas
|
|
10
|
+
- 📦 **TypeScript**: Fully typed with TypeScript for better DX
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @mkrlbs/mcp-adonisjs
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Available Tools
|
|
19
|
+
|
|
20
|
+
### 1. `make_controller`
|
|
21
|
+
|
|
22
|
+
Creates an AdonisJS controller using `node ace make:controller`.
|
|
23
|
+
|
|
24
|
+
**Arguments:**
|
|
25
|
+
- `name` (string, required): Name of the controller to create
|
|
26
|
+
- `resource` (boolean, optional): Whether to create a resource controller (default: false)
|
|
27
|
+
|
|
28
|
+
**Example:**
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"name": "UserController",
|
|
32
|
+
"resource": true
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. `make_service`
|
|
37
|
+
|
|
38
|
+
Creates an AdonisJS service using `node ace make:service`.
|
|
39
|
+
|
|
40
|
+
**Arguments:**
|
|
41
|
+
- `name` (string, required): Name of the service to create
|
|
42
|
+
|
|
43
|
+
**Example:**
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"name": "AuthService"
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. `run_ace_command`
|
|
51
|
+
|
|
52
|
+
Executes any AdonisJS Ace command with security checks.
|
|
53
|
+
|
|
54
|
+
**Arguments:**
|
|
55
|
+
- `command` (string, required): The Ace command to run (e.g., 'make:model')
|
|
56
|
+
- `args` (array of strings, optional): Arguments to pass to the command
|
|
57
|
+
|
|
58
|
+
**Example:**
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"command": "make:model",
|
|
62
|
+
"args": ["User", "--migration"]
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Security
|
|
67
|
+
|
|
68
|
+
### Blacklisted Commands
|
|
69
|
+
|
|
70
|
+
The following commands are blacklisted for security reasons:
|
|
71
|
+
- `db:wipe`
|
|
72
|
+
- `migration:fresh`
|
|
73
|
+
- `migration:refresh`
|
|
74
|
+
- `migration:reset`
|
|
75
|
+
|
|
76
|
+
Attempting to run these commands will result in an error.
|
|
77
|
+
|
|
78
|
+
### Shell Injection Protection
|
|
79
|
+
|
|
80
|
+
All command arguments are validated to prevent shell injection attacks. The following characters are not allowed:
|
|
81
|
+
- `;` (semicolon)
|
|
82
|
+
- `&` (ampersand)
|
|
83
|
+
- `|` (pipe)
|
|
84
|
+
- `` ` `` (backtick)
|
|
85
|
+
- `$` (dollar sign)
|
|
86
|
+
- `()` (parentheses)
|
|
87
|
+
- `{}` (curly braces)
|
|
88
|
+
- `[]` (square brackets)
|
|
89
|
+
- `<>` (angle brackets)
|
|
90
|
+
- `\` (backslash)
|
|
91
|
+
|
|
92
|
+
## Configuration
|
|
93
|
+
|
|
94
|
+
### Claude Desktop
|
|
95
|
+
|
|
96
|
+
Add to your `claude_desktop_config.json`:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"mcpServers": {
|
|
101
|
+
"adonisjs": {
|
|
102
|
+
"command": "npx",
|
|
103
|
+
"args": ["-y", "@mkrlbs/mcp-adonisjs"]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Other MCP Clients
|
|
110
|
+
|
|
111
|
+
The server uses stdio transport and can be configured with any MCP-compatible client. Provide the command:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx @mkrlbs/mcp-adonisjs
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
### Building
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npm run build
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Watch Mode
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm run watch
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Requirements
|
|
132
|
+
|
|
133
|
+
- Node.js 18+
|
|
134
|
+
- An AdonisJS project in the current working directory
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT
|
|
139
|
+
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
/**
|
|
8
|
+
* Blacklisted commands that are considered dangerous
|
|
9
|
+
*/
|
|
10
|
+
const BLACKLISTED_COMMANDS = [
|
|
11
|
+
"db:wipe",
|
|
12
|
+
"migration:fresh",
|
|
13
|
+
"migration:refresh",
|
|
14
|
+
"migration:reset",
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* Shell injection characters that are not allowed in command arguments
|
|
18
|
+
*/
|
|
19
|
+
const SHELL_INJECTION_PATTERN = /[;&|`$(){}[\]<>\\]/;
|
|
20
|
+
/**
|
|
21
|
+
* Validates that command arguments don't contain shell injection characters
|
|
22
|
+
*/
|
|
23
|
+
function validateArguments(args) {
|
|
24
|
+
for (const arg of args) {
|
|
25
|
+
if (SHELL_INJECTION_PATTERN.test(arg)) {
|
|
26
|
+
throw new Error(`Invalid argument "${arg}": contains potentially dangerous characters`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Checks if a command is blacklisted
|
|
32
|
+
*/
|
|
33
|
+
function isBlacklisted(command) {
|
|
34
|
+
return BLACKLISTED_COMMANDS.includes(command);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Executes an Ace command safely
|
|
38
|
+
*/
|
|
39
|
+
function executeAceCommand(command, args = []) {
|
|
40
|
+
// Check blacklist
|
|
41
|
+
if (isBlacklisted(command)) {
|
|
42
|
+
throw new Error(`Command "${command}" is blacklisted for security reasons. Blocked commands: ${BLACKLISTED_COMMANDS.join(", ")}`);
|
|
43
|
+
}
|
|
44
|
+
// Validate arguments
|
|
45
|
+
validateArguments(args);
|
|
46
|
+
// Build the command
|
|
47
|
+
const fullCommand = `node ace ${command} ${args.join(" ")}`.trim();
|
|
48
|
+
try {
|
|
49
|
+
const output = execSync(fullCommand, {
|
|
50
|
+
cwd: process.cwd(),
|
|
51
|
+
encoding: "utf-8",
|
|
52
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
53
|
+
});
|
|
54
|
+
return output;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw new Error(`Command execution failed: ${error.message}\nStderr: ${error.stderr || "N/A"}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Schema definitions for tool arguments
|
|
62
|
+
*/
|
|
63
|
+
const MakeControllerArgsSchema = z.object({
|
|
64
|
+
name: z.string().describe("Name of the controller to create"),
|
|
65
|
+
resource: z
|
|
66
|
+
.boolean()
|
|
67
|
+
.optional()
|
|
68
|
+
.default(false)
|
|
69
|
+
.describe("Whether to create a resource controller"),
|
|
70
|
+
});
|
|
71
|
+
const MakeServiceArgsSchema = z.object({
|
|
72
|
+
name: z.string().describe("Name of the service to create"),
|
|
73
|
+
});
|
|
74
|
+
const RunAceCommandArgsSchema = z.object({
|
|
75
|
+
command: z.string().describe("The Ace command to run (e.g., 'make:model')"),
|
|
76
|
+
args: z
|
|
77
|
+
.array(z.string())
|
|
78
|
+
.optional()
|
|
79
|
+
.default([])
|
|
80
|
+
.describe("Arguments to pass to the command"),
|
|
81
|
+
});
|
|
82
|
+
/**
|
|
83
|
+
* Initialize the MCP server
|
|
84
|
+
*/
|
|
85
|
+
const server = new Server({
|
|
86
|
+
name: "adonisjs-mcp-server",
|
|
87
|
+
version: "0.1.0",
|
|
88
|
+
}, {
|
|
89
|
+
capabilities: {
|
|
90
|
+
tools: {},
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
/**
|
|
94
|
+
* Handler for listing available tools
|
|
95
|
+
*/
|
|
96
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
97
|
+
return {
|
|
98
|
+
tools: [
|
|
99
|
+
{
|
|
100
|
+
name: "make_controller",
|
|
101
|
+
description: "Creates an AdonisJS controller using the 'node ace make:controller' command",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: {
|
|
105
|
+
name: {
|
|
106
|
+
type: "string",
|
|
107
|
+
description: "Name of the controller to create",
|
|
108
|
+
},
|
|
109
|
+
resource: {
|
|
110
|
+
type: "boolean",
|
|
111
|
+
description: "Whether to create a resource controller",
|
|
112
|
+
default: false,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
required: ["name"],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "make_service",
|
|
120
|
+
description: "Creates an AdonisJS service using the 'node ace make:service' command",
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {
|
|
124
|
+
name: {
|
|
125
|
+
type: "string",
|
|
126
|
+
description: "Name of the service to create",
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
required: ["name"],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "run_ace_command",
|
|
134
|
+
description: "Executes an AdonisJS Ace command with security checks. Blacklisted commands: " +
|
|
135
|
+
BLACKLISTED_COMMANDS.join(", "),
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: {
|
|
139
|
+
command: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: "The Ace command to run (e.g., 'make:model')",
|
|
142
|
+
},
|
|
143
|
+
args: {
|
|
144
|
+
type: "array",
|
|
145
|
+
items: {
|
|
146
|
+
type: "string",
|
|
147
|
+
},
|
|
148
|
+
description: "Arguments to pass to the command",
|
|
149
|
+
default: [],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
required: ["command"],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
/**
|
|
159
|
+
* Handler for executing tools
|
|
160
|
+
*/
|
|
161
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
162
|
+
try {
|
|
163
|
+
const { name, arguments: args } = request.params;
|
|
164
|
+
switch (name) {
|
|
165
|
+
case "make_controller": {
|
|
166
|
+
const parsed = MakeControllerArgsSchema.parse(args);
|
|
167
|
+
const commandArgs = parsed.resource ? [parsed.name, "--resource"] : [parsed.name];
|
|
168
|
+
const output = executeAceCommand("make:controller", commandArgs);
|
|
169
|
+
return {
|
|
170
|
+
content: [
|
|
171
|
+
{
|
|
172
|
+
type: "text",
|
|
173
|
+
text: `Controller created successfully:\n${output}`,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
case "make_service": {
|
|
179
|
+
const parsed = MakeServiceArgsSchema.parse(args);
|
|
180
|
+
const output = executeAceCommand("make:service", [parsed.name]);
|
|
181
|
+
return {
|
|
182
|
+
content: [
|
|
183
|
+
{
|
|
184
|
+
type: "text",
|
|
185
|
+
text: `Service created successfully:\n${output}`,
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
case "run_ace_command": {
|
|
191
|
+
const parsed = RunAceCommandArgsSchema.parse(args);
|
|
192
|
+
const output = executeAceCommand(parsed.command, parsed.args);
|
|
193
|
+
return {
|
|
194
|
+
content: [
|
|
195
|
+
{
|
|
196
|
+
type: "text",
|
|
197
|
+
text: `Command executed successfully:\n${output}`,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
default:
|
|
203
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
return {
|
|
208
|
+
content: [
|
|
209
|
+
{
|
|
210
|
+
type: "text",
|
|
211
|
+
text: `Error: ${error.message}`,
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
isError: true,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
/**
|
|
219
|
+
* Start the server
|
|
220
|
+
*/
|
|
221
|
+
async function main() {
|
|
222
|
+
const transport = new StdioServerTransport();
|
|
223
|
+
await server.connect(transport);
|
|
224
|
+
// Use stderr for logging since stdout is used for MCP protocol communication
|
|
225
|
+
console.error("AdonisJS MCP Server running on stdio");
|
|
226
|
+
}
|
|
227
|
+
main().catch((error) => {
|
|
228
|
+
console.error("Fatal error:", error);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
});
|
|
231
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mkrlbs/mcp-adonisjs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for executing AdonisJS Ace commands",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-adonisjs": "./build/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"build",
|
|
11
|
+
"src",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"prepare": "npm run build",
|
|
17
|
+
"watch": "tsc --watch",
|
|
18
|
+
"test": "node tests/security.test.js"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mcp",
|
|
22
|
+
"model-context-protocol",
|
|
23
|
+
"adonisjs",
|
|
24
|
+
"ace"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
30
|
+
"zod": "^3.23.8"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^22.10.2",
|
|
34
|
+
"typescript": "^5.7.2"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { execSync } from "child_process";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Blacklisted commands that are considered dangerous
|
|
14
|
+
*/
|
|
15
|
+
const BLACKLISTED_COMMANDS = [
|
|
16
|
+
"db:wipe",
|
|
17
|
+
"migration:fresh",
|
|
18
|
+
"migration:refresh",
|
|
19
|
+
"migration:reset",
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Shell injection characters that are not allowed in command arguments
|
|
24
|
+
*/
|
|
25
|
+
const SHELL_INJECTION_PATTERN = /[;&|`$(){}[\]<>\\]/;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Validates that command arguments don't contain shell injection characters
|
|
29
|
+
*/
|
|
30
|
+
function validateArguments(args: string[]): void {
|
|
31
|
+
for (const arg of args) {
|
|
32
|
+
if (SHELL_INJECTION_PATTERN.test(arg)) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Invalid argument "${arg}": contains potentially dangerous characters`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Checks if a command is blacklisted
|
|
42
|
+
*/
|
|
43
|
+
function isBlacklisted(command: string): boolean {
|
|
44
|
+
return BLACKLISTED_COMMANDS.includes(command);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Executes an Ace command safely
|
|
49
|
+
*/
|
|
50
|
+
function executeAceCommand(command: string, args: string[] = []): string {
|
|
51
|
+
// Check blacklist
|
|
52
|
+
if (isBlacklisted(command)) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Command "${command}" is blacklisted for security reasons. Blocked commands: ${BLACKLISTED_COMMANDS.join(", ")}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Validate arguments
|
|
59
|
+
validateArguments(args);
|
|
60
|
+
|
|
61
|
+
// Build the command
|
|
62
|
+
const fullCommand = `node ace ${command} ${args.join(" ")}`.trim();
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const output = execSync(fullCommand, {
|
|
66
|
+
cwd: process.cwd(),
|
|
67
|
+
encoding: "utf-8",
|
|
68
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
69
|
+
});
|
|
70
|
+
return output;
|
|
71
|
+
} catch (error: any) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`Command execution failed: ${error.message}\nStderr: ${error.stderr || "N/A"}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Schema definitions for tool arguments
|
|
80
|
+
*/
|
|
81
|
+
const MakeControllerArgsSchema = z.object({
|
|
82
|
+
name: z.string().describe("Name of the controller to create"),
|
|
83
|
+
resource: z
|
|
84
|
+
.boolean()
|
|
85
|
+
.optional()
|
|
86
|
+
.default(false)
|
|
87
|
+
.describe("Whether to create a resource controller"),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const MakeServiceArgsSchema = z.object({
|
|
91
|
+
name: z.string().describe("Name of the service to create"),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const RunAceCommandArgsSchema = z.object({
|
|
95
|
+
command: z.string().describe("The Ace command to run (e.g., 'make:model')"),
|
|
96
|
+
args: z
|
|
97
|
+
.array(z.string())
|
|
98
|
+
.optional()
|
|
99
|
+
.default([])
|
|
100
|
+
.describe("Arguments to pass to the command"),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Initialize the MCP server
|
|
105
|
+
*/
|
|
106
|
+
const server = new Server(
|
|
107
|
+
{
|
|
108
|
+
name: "adonisjs-mcp-server",
|
|
109
|
+
version: "0.1.0",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
capabilities: {
|
|
113
|
+
tools: {},
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Handler for listing available tools
|
|
120
|
+
*/
|
|
121
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
122
|
+
return {
|
|
123
|
+
tools: [
|
|
124
|
+
{
|
|
125
|
+
name: "make_controller",
|
|
126
|
+
description:
|
|
127
|
+
"Creates an AdonisJS controller using the 'node ace make:controller' command",
|
|
128
|
+
inputSchema: {
|
|
129
|
+
type: "object",
|
|
130
|
+
properties: {
|
|
131
|
+
name: {
|
|
132
|
+
type: "string",
|
|
133
|
+
description: "Name of the controller to create",
|
|
134
|
+
},
|
|
135
|
+
resource: {
|
|
136
|
+
type: "boolean",
|
|
137
|
+
description: "Whether to create a resource controller",
|
|
138
|
+
default: false,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
required: ["name"],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: "make_service",
|
|
146
|
+
description:
|
|
147
|
+
"Creates an AdonisJS service using the 'node ace make:service' command",
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: "object",
|
|
150
|
+
properties: {
|
|
151
|
+
name: {
|
|
152
|
+
type: "string",
|
|
153
|
+
description: "Name of the service to create",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
required: ["name"],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "run_ace_command",
|
|
161
|
+
description:
|
|
162
|
+
"Executes an AdonisJS Ace command with security checks. Blacklisted commands: " +
|
|
163
|
+
BLACKLISTED_COMMANDS.join(", "),
|
|
164
|
+
inputSchema: {
|
|
165
|
+
type: "object",
|
|
166
|
+
properties: {
|
|
167
|
+
command: {
|
|
168
|
+
type: "string",
|
|
169
|
+
description: "The Ace command to run (e.g., 'make:model')",
|
|
170
|
+
},
|
|
171
|
+
args: {
|
|
172
|
+
type: "array",
|
|
173
|
+
items: {
|
|
174
|
+
type: "string",
|
|
175
|
+
},
|
|
176
|
+
description: "Arguments to pass to the command",
|
|
177
|
+
default: [],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
required: ["command"],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Handler for executing tools
|
|
189
|
+
*/
|
|
190
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
191
|
+
try {
|
|
192
|
+
const { name, arguments: args } = request.params;
|
|
193
|
+
|
|
194
|
+
switch (name) {
|
|
195
|
+
case "make_controller": {
|
|
196
|
+
const parsed = MakeControllerArgsSchema.parse(args);
|
|
197
|
+
const commandArgs = parsed.resource ? [parsed.name, "--resource"] : [parsed.name];
|
|
198
|
+
const output = executeAceCommand("make:controller", commandArgs);
|
|
199
|
+
return {
|
|
200
|
+
content: [
|
|
201
|
+
{
|
|
202
|
+
type: "text",
|
|
203
|
+
text: `Controller created successfully:\n${output}`,
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
case "make_service": {
|
|
210
|
+
const parsed = MakeServiceArgsSchema.parse(args);
|
|
211
|
+
const output = executeAceCommand("make:service", [parsed.name]);
|
|
212
|
+
return {
|
|
213
|
+
content: [
|
|
214
|
+
{
|
|
215
|
+
type: "text",
|
|
216
|
+
text: `Service created successfully:\n${output}`,
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
case "run_ace_command": {
|
|
223
|
+
const parsed = RunAceCommandArgsSchema.parse(args);
|
|
224
|
+
const output = executeAceCommand(parsed.command, parsed.args);
|
|
225
|
+
return {
|
|
226
|
+
content: [
|
|
227
|
+
{
|
|
228
|
+
type: "text",
|
|
229
|
+
text: `Command executed successfully:\n${output}`,
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
default:
|
|
236
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
237
|
+
}
|
|
238
|
+
} catch (error: any) {
|
|
239
|
+
return {
|
|
240
|
+
content: [
|
|
241
|
+
{
|
|
242
|
+
type: "text",
|
|
243
|
+
text: `Error: ${error.message}`,
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
isError: true,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Start the server
|
|
253
|
+
*/
|
|
254
|
+
async function main() {
|
|
255
|
+
const transport = new StdioServerTransport();
|
|
256
|
+
await server.connect(transport);
|
|
257
|
+
// Use stderr for logging since stdout is used for MCP protocol communication
|
|
258
|
+
console.error("AdonisJS MCP Server running on stdio");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
main().catch((error) => {
|
|
262
|
+
console.error("Fatal error:", error);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
});
|