@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 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
+
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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
+ });