@type-crafter/mcp 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/LICENSE +15 -0
- package/README.md +337 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +354 -0
- package/package.json +74 -0
- package/src/GUIDE.md +365 -0
- package/src/SPEC_RULES.md +970 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Sahil Sinha
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# Type Crafter MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server for Type Crafter that allows AI assistants to generate type definitions from YAML specifications.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@type-crafter/mcp-server)
|
|
6
|
+
[](https://opensource.org/licenses/ISC)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
The MCP server exposes the following tools:
|
|
11
|
+
|
|
12
|
+
### 1. `generate-types`
|
|
13
|
+
|
|
14
|
+
Generate type definitions from a YAML specification file.
|
|
15
|
+
|
|
16
|
+
**Parameters:**
|
|
17
|
+
|
|
18
|
+
- `language` (required): Target language - `typescript` or `typescript-with-decoders`
|
|
19
|
+
- `specFilePath` (required): Path to the YAML specification file
|
|
20
|
+
- `outputDirectory` (required): Directory where generated types will be written
|
|
21
|
+
- `typesWriterMode` (optional): `SingleFile` or `Files` (default: `SingleFile`)
|
|
22
|
+
- `groupedTypesWriterMode` (optional): `FolderWithFiles` or `SingleFile` (default: `SingleFile`)
|
|
23
|
+
|
|
24
|
+
**Example:**
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"language": "typescript",
|
|
29
|
+
"specFilePath": "./types.yaml",
|
|
30
|
+
"outputDirectory": "./generated-types",
|
|
31
|
+
"typesWriterMode": "SingleFile",
|
|
32
|
+
"groupedTypesWriterMode": "FolderWithFiles"
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. `validate-spec`
|
|
37
|
+
|
|
38
|
+
Validate a YAML specification file without generating types.
|
|
39
|
+
|
|
40
|
+
**Parameters:**
|
|
41
|
+
|
|
42
|
+
- `specFilePath` (required): Path to the YAML specification file to validate
|
|
43
|
+
|
|
44
|
+
**Example:**
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"specFilePath": "./types.yaml"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. `list-languages`
|
|
53
|
+
|
|
54
|
+
List all supported target languages for type generation.
|
|
55
|
+
|
|
56
|
+
**Parameters:** None
|
|
57
|
+
|
|
58
|
+
### 4. `get-spec-info`
|
|
59
|
+
|
|
60
|
+
Get detailed information about a YAML specification file including version, title, and all defined types.
|
|
61
|
+
|
|
62
|
+
**Parameters:**
|
|
63
|
+
|
|
64
|
+
- `specFilePath` (required): Path to the YAML specification file
|
|
65
|
+
|
|
66
|
+
**Example:**
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"specFilePath": "./types.yaml"
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 5. `get-spec-rules`
|
|
75
|
+
|
|
76
|
+
Get comprehensive rules and guidelines for writing Type Crafter YAML specification files. This tool provides LLMs with detailed information about the YAML spec format, type mappings, nullable types, references, composition, best practices, and common patterns.
|
|
77
|
+
|
|
78
|
+
**Parameters:** None
|
|
79
|
+
|
|
80
|
+
**Use this tool when:**
|
|
81
|
+
|
|
82
|
+
- You need to create a new YAML specification file
|
|
83
|
+
- You want to understand the Type Crafter spec format
|
|
84
|
+
- You need guidance on type mappings and nullable types
|
|
85
|
+
- You want to learn about references, composition, and best practices
|
|
86
|
+
|
|
87
|
+
**Returns:** Complete specification rules documentation including:
|
|
88
|
+
|
|
89
|
+
- Root structure requirements
|
|
90
|
+
- Data types and TypeScript mapping
|
|
91
|
+
- Nullable types rules (properties not in `required` become `Type | null`)
|
|
92
|
+
- Type definitions (objects, enums, arrays, nested objects)
|
|
93
|
+
- References (local, external, cyclic)
|
|
94
|
+
- Composition (oneOf unions, allOf intersections)
|
|
95
|
+
- Best practices and common patterns
|
|
96
|
+
- Complete examples with YAML and TypeScript side-by-side
|
|
97
|
+
|
|
98
|
+
## Prerequisites
|
|
99
|
+
|
|
100
|
+
- Node.js >= 18.0.0
|
|
101
|
+
- `type-crafter` CLI installed globally or locally
|
|
102
|
+
|
|
103
|
+
Install Type Crafter CLI:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm install -g type-crafter
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Installation
|
|
110
|
+
|
|
111
|
+
### Option 1: Install from npm (Recommended)
|
|
112
|
+
|
|
113
|
+
Install the MCP server globally:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm install -g @type-crafter/mcp-server
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or install locally in your project:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npm install @type-crafter/mcp-server
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Option 2: Install from Source
|
|
126
|
+
|
|
127
|
+
1. Clone the repository:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git clone https://github.com/sinha-sahil/type-crafter.git
|
|
131
|
+
cd type-crafter/mcp-server
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
2. Install dependencies:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm install
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
3. Build the server:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm run build
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Configuration
|
|
147
|
+
|
|
148
|
+
### Using with Claude Desktop (Global Installation)
|
|
149
|
+
|
|
150
|
+
After installing globally via npm:
|
|
151
|
+
|
|
152
|
+
Edit your Claude Desktop configuration file:
|
|
153
|
+
|
|
154
|
+
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
155
|
+
|
|
156
|
+
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
157
|
+
|
|
158
|
+
**For global installation:**
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"mcpServers": {
|
|
163
|
+
"type-crafter": {
|
|
164
|
+
"command": "type-crafter-mcp"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**For local installation (from source):**
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"mcpServers": {
|
|
175
|
+
"type-crafter": {
|
|
176
|
+
"command": "node",
|
|
177
|
+
"args": ["/absolute/path/to/type-crafter/mcp-server/dist/index.js"]
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**For project-local npm installation:**
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"mcpServers": {
|
|
188
|
+
"type-crafter": {
|
|
189
|
+
"command": "npx",
|
|
190
|
+
"args": ["type-crafter-mcp"],
|
|
191
|
+
"cwd": "/path/to/your/project"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Usage Examples
|
|
198
|
+
|
|
199
|
+
Once configured, you can use the MCP server through Claude Desktop:
|
|
200
|
+
|
|
201
|
+
1. **Get specification rules (before writing specs):**
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
Show me the rules for writing Type Crafter YAML specification files
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
2. **Generate TypeScript types:**
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
Generate TypeScript types from my spec.yaml file and save them to ./types
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
3. **Validate a specification:**
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
Validate my types.yaml specification file
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
4. **Get spec information:**
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
Show me information about the types defined in my spec.yaml
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
5. **List available languages:**
|
|
226
|
+
```
|
|
227
|
+
What languages does Type Crafter support?
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## YAML Specification Format
|
|
231
|
+
|
|
232
|
+
The YAML specification should follow this structure:
|
|
233
|
+
|
|
234
|
+
```yaml
|
|
235
|
+
info:
|
|
236
|
+
version: 0.0.0
|
|
237
|
+
title: Title of your specification
|
|
238
|
+
|
|
239
|
+
types:
|
|
240
|
+
TypeName:
|
|
241
|
+
type: object
|
|
242
|
+
properties:
|
|
243
|
+
propertyName:
|
|
244
|
+
type: string
|
|
245
|
+
|
|
246
|
+
groupedTypes:
|
|
247
|
+
GroupName:
|
|
248
|
+
TypeInGroup:
|
|
249
|
+
type: object
|
|
250
|
+
properties:
|
|
251
|
+
id:
|
|
252
|
+
type: string
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
For more details on the specification format, see the [main Type Crafter README](../README.md).
|
|
256
|
+
|
|
257
|
+
## Development
|
|
258
|
+
|
|
259
|
+
### Build
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
npm run build
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Watch mode
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
npm run dev
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Development
|
|
272
|
+
|
|
273
|
+
### Building from Source
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
npm run build
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Watch Mode
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
npm run dev
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Linting
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
npm run lint
|
|
289
|
+
npm run lint:fix
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Testing the Package Locally
|
|
293
|
+
|
|
294
|
+
Before publishing or to test the installed package:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# Create a tarball
|
|
298
|
+
npm pack
|
|
299
|
+
|
|
300
|
+
# Install globally from tarball
|
|
301
|
+
npm install -g ./type-crafter-mcp-server-0.1.0.tgz
|
|
302
|
+
|
|
303
|
+
# Test it
|
|
304
|
+
type-crafter-mcp
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
See [PUBLISHING.md](./PUBLISHING.md) for detailed publishing instructions.
|
|
308
|
+
|
|
309
|
+
## Troubleshooting
|
|
310
|
+
|
|
311
|
+
### Server not connecting
|
|
312
|
+
|
|
313
|
+
- **Global installation:** Ensure `type-crafter-mcp` is in your PATH
|
|
314
|
+
- **Local installation:** Verify the path in your configuration is correct
|
|
315
|
+
- Check that the server was built successfully (`npm run build`)
|
|
316
|
+
- Restart Claude Desktop after modifying the configuration
|
|
317
|
+
- Check Node.js version (requires >= 18.0.0)
|
|
318
|
+
|
|
319
|
+
### Type generation errors
|
|
320
|
+
|
|
321
|
+
- Ensure `type-crafter` CLI is installed: `npm install -g type-crafter`
|
|
322
|
+
- Validate your YAML specification using the `validate-spec` tool
|
|
323
|
+
- Check that all file paths are absolute or relative to your working directory
|
|
324
|
+
- Ensure the output directory exists or the server has permissions to create it
|
|
325
|
+
|
|
326
|
+
### Permission denied error
|
|
327
|
+
|
|
328
|
+
- For global installation, you may need sudo: `sudo npm install -g @type-crafter/mcp-server`
|
|
329
|
+
- Or configure npm to install global packages in your home directory
|
|
330
|
+
|
|
331
|
+
## Contributing
|
|
332
|
+
|
|
333
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
334
|
+
|
|
335
|
+
## License
|
|
336
|
+
|
|
337
|
+
ISC - See [LICENSE](./LICENSE) file for details
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { parse as parseYaml } from 'yaml';
|
|
7
|
+
import { exec } from 'child_process';
|
|
8
|
+
import { promisify } from 'util';
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
// Helper function to read YAML files
|
|
11
|
+
async function readYaml(filePath) {
|
|
12
|
+
const fileContent = await fs.readFile(filePath, 'utf-8');
|
|
13
|
+
return parseYaml(fileContent);
|
|
14
|
+
}
|
|
15
|
+
// Helper function to validate spec structure
|
|
16
|
+
function validateSpecData(data) {
|
|
17
|
+
if (typeof data !== 'object' || data === null) {
|
|
18
|
+
return { valid: false };
|
|
19
|
+
}
|
|
20
|
+
const spec = data;
|
|
21
|
+
// Check for required info
|
|
22
|
+
if (typeof spec.info !== 'object' || spec.info === null) {
|
|
23
|
+
return { valid: false };
|
|
24
|
+
}
|
|
25
|
+
const info = spec.info;
|
|
26
|
+
if (typeof info.version !== 'string' || typeof info.title !== 'string') {
|
|
27
|
+
return { valid: false };
|
|
28
|
+
}
|
|
29
|
+
// At least one of types or groupedTypes must exist
|
|
30
|
+
const hasTypes = typeof spec.types === 'object' && spec.types !== null;
|
|
31
|
+
const hasGroupedTypes = typeof spec.groupedTypes === 'object' && spec.groupedTypes !== null;
|
|
32
|
+
if (!hasTypes && !hasGroupedTypes) {
|
|
33
|
+
return { valid: false };
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
valid: true,
|
|
37
|
+
info: { version: info.version, title: info.title },
|
|
38
|
+
types: hasTypes ? spec.types : undefined,
|
|
39
|
+
groupedTypes: hasGroupedTypes ? spec.groupedTypes : undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Create server instance
|
|
43
|
+
const server = new McpServer({
|
|
44
|
+
name: 'type-crafter-mcp-server',
|
|
45
|
+
version: '0.1.0',
|
|
46
|
+
}, {
|
|
47
|
+
capabilities: {
|
|
48
|
+
tools: {},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
// Register generate-types tool
|
|
52
|
+
server.registerTool('generate-types', {
|
|
53
|
+
description: 'Generate type definitions from a YAML specification file. Supports TypeScript and TypeScript with decoders. ' +
|
|
54
|
+
'The YAML spec should follow the Type Crafter format with info, types, and/or groupedTypes sections.',
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
language: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'Target language for type generation',
|
|
61
|
+
enum: ['typescript', 'typescript-with-decoders'],
|
|
62
|
+
},
|
|
63
|
+
specFilePath: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'Path to the YAML specification file',
|
|
66
|
+
},
|
|
67
|
+
outputDirectory: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
description: 'Directory where generated types will be written',
|
|
70
|
+
},
|
|
71
|
+
typesWriterMode: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
description: 'Writer mode for types: SingleFile or Files',
|
|
74
|
+
enum: ['SingleFile', 'Files'],
|
|
75
|
+
},
|
|
76
|
+
groupedTypesWriterMode: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
description: 'Writer mode for grouped types: FolderWithFiles or SingleFile',
|
|
79
|
+
enum: ['FolderWithFiles', 'SingleFile'],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
required: ['language', 'specFilePath', 'outputDirectory'],
|
|
83
|
+
},
|
|
84
|
+
}, async (args) => {
|
|
85
|
+
const { language, specFilePath, outputDirectory, typesWriterMode = 'SingleFile', groupedTypesWriterMode = 'SingleFile', } = args;
|
|
86
|
+
// Validate language
|
|
87
|
+
if (!['typescript', 'typescript-with-decoders'].includes(language.toLowerCase())) {
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: 'text',
|
|
92
|
+
text: `Error: Unsupported language "${language}". Supported languages: typescript, typescript-with-decoders`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
isError: true,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// Resolve paths
|
|
99
|
+
const resolvedSpecPath = path.resolve(specFilePath);
|
|
100
|
+
const resolvedOutputPath = path.resolve(outputDirectory);
|
|
101
|
+
// Check if spec file exists
|
|
102
|
+
try {
|
|
103
|
+
await fs.access(resolvedSpecPath);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: 'text',
|
|
110
|
+
text: `Error: Specification file not found at ${resolvedSpecPath}`,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
isError: true,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Use the type-crafter CLI to generate types
|
|
117
|
+
const cliCommand = `type-crafter generate ${language} "${resolvedSpecPath}" "${resolvedOutputPath}" ${typesWriterMode} ${groupedTypesWriterMode}`;
|
|
118
|
+
try {
|
|
119
|
+
const { stdout, stderr } = await execAsync(cliCommand);
|
|
120
|
+
const warningText = typeof stderr !== 'undefined' && stderr.length > 0 ? '\nWarnings:\n' + stderr : '';
|
|
121
|
+
return {
|
|
122
|
+
content: [
|
|
123
|
+
{
|
|
124
|
+
type: 'text',
|
|
125
|
+
text: `Successfully generated ${language} types from ${specFilePath}\nOutput saved to: ${outputDirectory}\n\n${stdout}${warningText}`,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
const err = error;
|
|
132
|
+
const errorDetails = err.stderr ?? err.stdout ?? '';
|
|
133
|
+
return {
|
|
134
|
+
content: [
|
|
135
|
+
{
|
|
136
|
+
type: 'text',
|
|
137
|
+
text: `Error generating types: ${err.message}\n${errorDetails}`,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
isError: true,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
// Register validate-spec tool
|
|
145
|
+
server.registerTool('validate-spec', {
|
|
146
|
+
description: 'Validate a YAML specification file without generating types. ' +
|
|
147
|
+
'Checks if the spec file is valid and can be processed by Type Crafter.',
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: 'object',
|
|
150
|
+
properties: {
|
|
151
|
+
specFilePath: {
|
|
152
|
+
type: 'string',
|
|
153
|
+
description: 'Path to the YAML specification file to validate',
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
required: ['specFilePath'],
|
|
157
|
+
},
|
|
158
|
+
}, async (args) => {
|
|
159
|
+
const { specFilePath } = args;
|
|
160
|
+
// Resolve path
|
|
161
|
+
const resolvedSpecPath = path.resolve(specFilePath);
|
|
162
|
+
// Check if spec file exists
|
|
163
|
+
try {
|
|
164
|
+
await fs.access(resolvedSpecPath);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return {
|
|
168
|
+
content: [
|
|
169
|
+
{
|
|
170
|
+
type: 'text',
|
|
171
|
+
text: `Error: Specification file not found at ${resolvedSpecPath}`,
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
isError: true,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// Try to read and validate the spec
|
|
178
|
+
const specFileData = await readYaml(resolvedSpecPath);
|
|
179
|
+
const validation = validateSpecData(specFileData);
|
|
180
|
+
if (!validation.valid || typeof validation.info === 'undefined') {
|
|
181
|
+
return {
|
|
182
|
+
content: [
|
|
183
|
+
{
|
|
184
|
+
type: 'text',
|
|
185
|
+
text: 'Error: Invalid specification file. Neither types nor groupedTypes found!',
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
isError: true,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const typesCount = typeof validation.types !== 'undefined' ? Object.keys(validation.types).length : 0;
|
|
192
|
+
const groupedTypesCount = typeof validation.groupedTypes !== 'undefined'
|
|
193
|
+
? Object.keys(validation.groupedTypes).length
|
|
194
|
+
: 0;
|
|
195
|
+
return {
|
|
196
|
+
content: [
|
|
197
|
+
{
|
|
198
|
+
type: 'text',
|
|
199
|
+
text: `✓ Specification file is valid!\n\nInfo:\n Version: ${validation.info.version}\n Title: ${validation.info.title}\n Types: ${typesCount}\n Grouped Types: ${groupedTypesCount}`,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
// Register list-languages tool
|
|
205
|
+
server.registerTool('list-languages', {
|
|
206
|
+
description: 'List all supported target languages for type generation',
|
|
207
|
+
inputSchema: {
|
|
208
|
+
type: 'object',
|
|
209
|
+
properties: {},
|
|
210
|
+
},
|
|
211
|
+
}, async () => {
|
|
212
|
+
return {
|
|
213
|
+
content: [
|
|
214
|
+
{
|
|
215
|
+
type: 'text',
|
|
216
|
+
text: 'Supported Languages:\n\n1. typescript - Generate TypeScript type definitions\n2. typescript-with-decoders - Generate TypeScript types with runtime decoders',
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
// Register get-spec-info tool
|
|
222
|
+
server.registerTool('get-spec-info', {
|
|
223
|
+
description: 'Get information about a YAML specification file including version, title, ' +
|
|
224
|
+
'and counts of types and grouped types defined in the spec.',
|
|
225
|
+
inputSchema: {
|
|
226
|
+
type: 'object',
|
|
227
|
+
properties: {
|
|
228
|
+
specFilePath: {
|
|
229
|
+
type: 'string',
|
|
230
|
+
description: 'Path to the YAML specification file',
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
required: ['specFilePath'],
|
|
234
|
+
},
|
|
235
|
+
}, async (args) => {
|
|
236
|
+
const { specFilePath } = args;
|
|
237
|
+
// Resolve path
|
|
238
|
+
const resolvedSpecPath = path.resolve(specFilePath);
|
|
239
|
+
// Check if spec file exists
|
|
240
|
+
try {
|
|
241
|
+
await fs.access(resolvedSpecPath);
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
return {
|
|
245
|
+
content: [
|
|
246
|
+
{
|
|
247
|
+
type: 'text',
|
|
248
|
+
text: `Error: Specification file not found at ${resolvedSpecPath}`,
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
isError: true,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
// Read and validate the spec
|
|
255
|
+
const specFileData = await readYaml(resolvedSpecPath);
|
|
256
|
+
const validation = validateSpecData(specFileData);
|
|
257
|
+
if (!validation.valid || typeof validation.info === 'undefined') {
|
|
258
|
+
return {
|
|
259
|
+
content: [
|
|
260
|
+
{
|
|
261
|
+
type: 'text',
|
|
262
|
+
text: 'Error: Invalid specification file format',
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
isError: true,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
const typesCount = typeof validation.types !== 'undefined' ? Object.keys(validation.types).length : 0;
|
|
269
|
+
const groupedTypesCount = typeof validation.groupedTypes !== 'undefined'
|
|
270
|
+
? Object.keys(validation.groupedTypes).length
|
|
271
|
+
: 0;
|
|
272
|
+
let infoText = 'Specification Info:\n\n';
|
|
273
|
+
infoText += `Version: ${validation.info.version}\n`;
|
|
274
|
+
infoText += `Title: ${validation.info.title}\n`;
|
|
275
|
+
infoText += `Types: ${typesCount}\n`;
|
|
276
|
+
infoText += `Grouped Types: ${groupedTypesCount}\n\n`;
|
|
277
|
+
if (typeof validation.types !== 'undefined' && typesCount > 0) {
|
|
278
|
+
infoText += 'Top-level Types:\n';
|
|
279
|
+
Object.keys(validation.types).forEach((typeName) => {
|
|
280
|
+
infoText += ` - ${typeName}\n`;
|
|
281
|
+
});
|
|
282
|
+
infoText += '\n';
|
|
283
|
+
}
|
|
284
|
+
if (typeof validation.groupedTypes !== 'undefined' && groupedTypesCount > 0) {
|
|
285
|
+
infoText += 'Grouped Types:\n';
|
|
286
|
+
Object.keys(validation.groupedTypes).forEach((groupName) => {
|
|
287
|
+
const group = validation.groupedTypes?.[groupName];
|
|
288
|
+
if (typeof group !== 'undefined' && group !== null && typeof group === 'object' && !('$ref' in group)) {
|
|
289
|
+
const typeNames = Object.keys(group);
|
|
290
|
+
infoText += ` ${groupName} (${typeNames.length} types):\n`;
|
|
291
|
+
typeNames.forEach((typeName) => {
|
|
292
|
+
infoText += ` - ${typeName}\n`;
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
infoText += ` ${groupName} (reference)\n`;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
content: [
|
|
302
|
+
{
|
|
303
|
+
type: 'text',
|
|
304
|
+
text: infoText,
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
};
|
|
308
|
+
});
|
|
309
|
+
// Register get-spec-rules tool
|
|
310
|
+
server.registerTool('get-spec-rules', {
|
|
311
|
+
description: 'Get comprehensive rules and guidelines for writing Type Crafter YAML specification files. ' +
|
|
312
|
+
'This provides LLMs with detailed information about the YAML spec format, type mappings, nullable types, ' +
|
|
313
|
+
'references, composition, best practices, and common patterns. Use this before creating or modifying spec files.',
|
|
314
|
+
inputSchema: {
|
|
315
|
+
type: 'object',
|
|
316
|
+
properties: {},
|
|
317
|
+
},
|
|
318
|
+
}, async () => {
|
|
319
|
+
try {
|
|
320
|
+
// Read the SPEC_RULES.md file from src directory
|
|
321
|
+
const rulesPath = path.join(__dirname, '..', 'src', 'SPEC_RULES.md');
|
|
322
|
+
const rulesContent = await fs.readFile(rulesPath, 'utf-8');
|
|
323
|
+
return {
|
|
324
|
+
content: [
|
|
325
|
+
{
|
|
326
|
+
type: 'text',
|
|
327
|
+
text: rulesContent,
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
const err = error;
|
|
334
|
+
return {
|
|
335
|
+
content: [
|
|
336
|
+
{
|
|
337
|
+
type: 'text',
|
|
338
|
+
text: `Error reading spec rules: ${err.message}`,
|
|
339
|
+
},
|
|
340
|
+
],
|
|
341
|
+
isError: true,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
// Start the server
|
|
346
|
+
async function main() {
|
|
347
|
+
const transport = new StdioServerTransport();
|
|
348
|
+
await server.connect(transport);
|
|
349
|
+
console.error('Type Crafter MCP Server running on stdio');
|
|
350
|
+
}
|
|
351
|
+
main().catch((error) => {
|
|
352
|
+
console.error('Server error:', error);
|
|
353
|
+
process.exit(1);
|
|
354
|
+
});
|