@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 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
+ [![npm version](https://badge.fury.io/js/@type-crafter%2Fmcp-server.svg)](https://www.npmjs.com/package/@type-crafter/mcp-server)
6
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](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
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
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
+ });