@type-crafter/mcp 0.1.0 → 0.2.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.
Files changed (2) hide show
  1. package/dist/index.js +162 -74
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -7,6 +7,27 @@ import { parse as parseYaml } from 'yaml';
7
7
  import { exec } from 'child_process';
8
8
  import { promisify } from 'util';
9
9
  const execAsync = promisify(exec);
10
+ // Type guards
11
+ function isRecord(value) {
12
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
13
+ }
14
+ function isSpecInfo(value) {
15
+ return (isRecord(value) &&
16
+ typeof value.version === 'string' &&
17
+ typeof value.title === 'string');
18
+ }
19
+ function isExecError(error) {
20
+ return error instanceof Error;
21
+ }
22
+ function isGenerateTypesArgs(value) {
23
+ return (isRecord(value) &&
24
+ typeof value.language === 'string' &&
25
+ typeof value.specFilePath === 'string' &&
26
+ typeof value.outputDirectory === 'string');
27
+ }
28
+ function isSpecFilePathArgs(value) {
29
+ return isRecord(value) && typeof value.specFilePath === 'string';
30
+ }
10
31
  // Helper function to read YAML files
11
32
  async function readYaml(filePath) {
12
33
  const fileContent = await fs.readFile(filePath, 'utf-8');
@@ -14,29 +35,27 @@ async function readYaml(filePath) {
14
35
  }
15
36
  // Helper function to validate spec structure
16
37
  function validateSpecData(data) {
17
- if (typeof data !== 'object' || data === null) {
38
+ if (!isRecord(data)) {
18
39
  return { valid: false };
19
40
  }
20
- const spec = data;
21
41
  // Check for required info
22
- if (typeof spec.info !== 'object' || spec.info === null) {
42
+ if (!isRecord(data.info)) {
23
43
  return { valid: false };
24
44
  }
25
- const info = spec.info;
26
- if (typeof info.version !== 'string' || typeof info.title !== 'string') {
45
+ if (!isSpecInfo(data.info)) {
27
46
  return { valid: false };
28
47
  }
29
48
  // 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) {
49
+ const types = isRecord(data.types) ? data.types : undefined;
50
+ const groupedTypes = isRecord(data.groupedTypes) ? data.groupedTypes : undefined;
51
+ if (typeof types === 'undefined' && typeof groupedTypes === 'undefined') {
33
52
  return { valid: false };
34
53
  }
35
54
  return {
36
55
  valid: true,
37
- info: { version: info.version, title: info.title },
38
- types: hasTypes ? spec.types : undefined,
39
- groupedTypes: hasGroupedTypes ? spec.groupedTypes : undefined,
56
+ info: data.info,
57
+ types,
58
+ groupedTypes,
40
59
  };
41
60
  }
42
61
  // Create server instance
@@ -48,40 +67,87 @@ const server = new McpServer({
48
67
  tools: {},
49
68
  },
50
69
  });
70
+ // Schema definitions for all tools
71
+ const generateTypesSchema = {
72
+ type: 'object',
73
+ properties: {
74
+ language: {
75
+ type: 'string',
76
+ description: 'Target language for type generation',
77
+ enum: ['typescript', 'typescript-with-decoders'],
78
+ },
79
+ specFilePath: {
80
+ type: 'string',
81
+ description: 'Path to the YAML specification file',
82
+ },
83
+ outputDirectory: {
84
+ type: 'string',
85
+ description: 'Directory where generated types will be written',
86
+ },
87
+ typesWriterMode: {
88
+ type: 'string',
89
+ description: 'Writer mode for types: SingleFile or Files',
90
+ enum: ['SingleFile', 'Files'],
91
+ },
92
+ groupedTypesWriterMode: {
93
+ type: 'string',
94
+ description: 'Writer mode for grouped types: FolderWithFiles or SingleFile',
95
+ enum: ['FolderWithFiles', 'SingleFile'],
96
+ },
97
+ },
98
+ required: ['language', 'specFilePath', 'outputDirectory'],
99
+ additionalProperties: false,
100
+ };
101
+ const validateSpecSchema = {
102
+ type: 'object',
103
+ properties: {
104
+ specFilePath: {
105
+ type: 'string',
106
+ description: 'Path to the YAML specification file to validate',
107
+ },
108
+ },
109
+ required: ['specFilePath'],
110
+ additionalProperties: false,
111
+ };
112
+ const listLanguagesSchema = {
113
+ type: 'object',
114
+ properties: {},
115
+ additionalProperties: false,
116
+ };
117
+ const getSpecInfoSchema = {
118
+ type: 'object',
119
+ properties: {
120
+ specFilePath: {
121
+ type: 'string',
122
+ description: 'Path to the YAML specification file',
123
+ },
124
+ },
125
+ required: ['specFilePath'],
126
+ additionalProperties: false,
127
+ };
128
+ const getSpecRulesSchema = {
129
+ type: 'object',
130
+ properties: {},
131
+ additionalProperties: false,
132
+ };
51
133
  // Register generate-types tool
52
134
  server.registerTool('generate-types', {
53
135
  description: 'Generate type definitions from a YAML specification file. Supports TypeScript and TypeScript with decoders. ' +
54
136
  '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
- },
137
+ // @ts-expect-error - MCP SDK schema type mismatch
138
+ inputSchema: generateTypesSchema,
84
139
  }, async (args) => {
140
+ if (!isGenerateTypesArgs(args)) {
141
+ return {
142
+ content: [
143
+ {
144
+ type: 'text',
145
+ text: 'Error: Invalid arguments provided',
146
+ },
147
+ ],
148
+ isError: true,
149
+ };
150
+ }
85
151
  const { language, specFilePath, outputDirectory, typesWriterMode = 'SingleFile', groupedTypesWriterMode = 'SingleFile', } = args;
86
152
  // Validate language
87
153
  if (!['typescript', 'typescript-with-decoders'].includes(language.toLowerCase())) {
@@ -128,13 +194,23 @@ server.registerTool('generate-types', {
128
194
  };
129
195
  }
130
196
  catch (error) {
131
- const err = error;
132
- const errorDetails = err.stderr ?? err.stdout ?? '';
197
+ if (!isExecError(error)) {
198
+ return {
199
+ content: [
200
+ {
201
+ type: 'text',
202
+ text: 'Error: Unknown error occurred during type generation',
203
+ },
204
+ ],
205
+ isError: true,
206
+ };
207
+ }
208
+ const errorDetails = error.stderr ?? error.stdout ?? '';
133
209
  return {
134
210
  content: [
135
211
  {
136
212
  type: 'text',
137
- text: `Error generating types: ${err.message}\n${errorDetails}`,
213
+ text: `Error generating types: ${error.message}\n${errorDetails}`,
138
214
  },
139
215
  ],
140
216
  isError: true,
@@ -145,17 +221,20 @@ server.registerTool('generate-types', {
145
221
  server.registerTool('validate-spec', {
146
222
  description: 'Validate a YAML specification file without generating types. ' +
147
223
  '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
- },
224
+ // @ts-expect-error - MCP SDK schema type mismatch
225
+ inputSchema: validateSpecSchema,
158
226
  }, async (args) => {
227
+ if (!isSpecFilePathArgs(args)) {
228
+ return {
229
+ content: [
230
+ {
231
+ type: 'text',
232
+ text: 'Error: Invalid arguments provided',
233
+ },
234
+ ],
235
+ isError: true,
236
+ };
237
+ }
159
238
  const { specFilePath } = args;
160
239
  // Resolve path
161
240
  const resolvedSpecPath = path.resolve(specFilePath);
@@ -204,10 +283,8 @@ server.registerTool('validate-spec', {
204
283
  // Register list-languages tool
205
284
  server.registerTool('list-languages', {
206
285
  description: 'List all supported target languages for type generation',
207
- inputSchema: {
208
- type: 'object',
209
- properties: {},
210
- },
286
+ // @ts-expect-error - MCP SDK schema type mismatch
287
+ inputSchema: listLanguagesSchema,
211
288
  }, async () => {
212
289
  return {
213
290
  content: [
@@ -222,17 +299,20 @@ server.registerTool('list-languages', {
222
299
  server.registerTool('get-spec-info', {
223
300
  description: 'Get information about a YAML specification file including version, title, ' +
224
301
  '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
- },
302
+ // @ts-expect-error - MCP SDK schema type mismatch
303
+ inputSchema: getSpecInfoSchema,
235
304
  }, async (args) => {
305
+ if (!isSpecFilePathArgs(args)) {
306
+ return {
307
+ content: [
308
+ {
309
+ type: 'text',
310
+ text: 'Error: Invalid arguments provided',
311
+ },
312
+ ],
313
+ isError: true,
314
+ };
315
+ }
236
316
  const { specFilePath } = args;
237
317
  // Resolve path
238
318
  const resolvedSpecPath = path.resolve(specFilePath);
@@ -285,7 +365,7 @@ server.registerTool('get-spec-info', {
285
365
  infoText += 'Grouped Types:\n';
286
366
  Object.keys(validation.groupedTypes).forEach((groupName) => {
287
367
  const group = validation.groupedTypes?.[groupName];
288
- if (typeof group !== 'undefined' && group !== null && typeof group === 'object' && !('$ref' in group)) {
368
+ if (isRecord(group) && !('$ref' in group)) {
289
369
  const typeNames = Object.keys(group);
290
370
  infoText += ` ${groupName} (${typeNames.length} types):\n`;
291
371
  typeNames.forEach((typeName) => {
@@ -311,10 +391,8 @@ server.registerTool('get-spec-rules', {
311
391
  description: 'Get comprehensive rules and guidelines for writing Type Crafter YAML specification files. ' +
312
392
  'This provides LLMs with detailed information about the YAML spec format, type mappings, nullable types, ' +
313
393
  'references, composition, best practices, and common patterns. Use this before creating or modifying spec files.',
314
- inputSchema: {
315
- type: 'object',
316
- properties: {},
317
- },
394
+ // @ts-expect-error - MCP SDK schema type mismatch
395
+ inputSchema: getSpecRulesSchema,
318
396
  }, async () => {
319
397
  try {
320
398
  // Read the SPEC_RULES.md file from src directory
@@ -330,12 +408,22 @@ server.registerTool('get-spec-rules', {
330
408
  };
331
409
  }
332
410
  catch (error) {
333
- const err = error;
411
+ if (!isExecError(error)) {
412
+ return {
413
+ content: [
414
+ {
415
+ type: 'text',
416
+ text: 'Error: Unknown error occurred while reading spec rules',
417
+ },
418
+ ],
419
+ isError: true,
420
+ };
421
+ }
334
422
  return {
335
423
  content: [
336
424
  {
337
425
  type: 'text',
338
- text: `Error reading spec rules: ${err.message}`,
426
+ text: `Error reading spec rules: ${error.message}`,
339
427
  },
340
428
  ],
341
429
  isError: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@type-crafter/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server for Type Crafter - generate types from YAML specifications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,7 +60,7 @@
60
60
  }
61
61
  },
62
62
  "dependencies": {
63
- "@modelcontextprotocol/sdk": "^1.0.4",
63
+ "@modelcontextprotocol/sdk": "^1.25.1",
64
64
  "yaml": "^2.3.2"
65
65
  },
66
66
  "devDependencies": {