|
8 | 8 | using Azure.Sdk.Tools.Cli.Models.Responses; |
9 | 9 | using ModelContextProtocol.Server; |
10 | 10 | using Azure.Sdk.Tools.Cli.Helpers; |
| 11 | +using Azure.Sdk.Tools.Cli.Commands; |
11 | 12 |
|
12 | 13 | namespace Azure.Sdk.Tools.Cli.Tools.TypeSpec |
13 | 14 | { |
14 | 15 | /// <summary> |
15 | | - /// This tool provides functionality for initializing TypeSpec projects and converting existing Azure service swagger definitions to TypeSpec projects. |
16 | | - /// Use this tool to onboard to TypeSpec for new services or convert existing services. |
| 16 | + /// This tool provides functionality for converting existing Azure service swagger definitions to TypeSpec projects. |
| 17 | + /// Use this tool to convert existing services to TypeSpec. |
17 | 18 | /// </summary> |
18 | | - [McpServerToolType, Description("Tools for initializing TypeSpec projects and converting existing Azure service swagger definitions to TypeSpec projects.")] |
19 | | - public class TypeSpecTool(INpxHelper npxHelper, ILogger<TypeSpecTool> logger, IOutputHelper output) : MCPTool |
| 19 | + [McpServerToolType, Description("Tools for converting existing Azure service swagger definitions to TypeSpec projects.")] |
| 20 | + public class TypeSpecConvertTool : MCPTool |
20 | 21 | { |
| 22 | + private readonly INpxHelper npxHelper; |
| 23 | + private readonly ILogger<TypeSpecConvertTool> logger; |
| 24 | + private readonly IOutputHelper output; |
| 25 | + |
| 26 | + public TypeSpecConvertTool(INpxHelper npxHelper, ILogger<TypeSpecConvertTool> logger, IOutputHelper output) |
| 27 | + { |
| 28 | + this.npxHelper = npxHelper; |
| 29 | + this.logger = logger; |
| 30 | + this.output = output; |
| 31 | + CommandHierarchy = [SharedCommandGroups.TypeSpec]; |
| 32 | + } |
21 | 33 |
|
22 | 34 | // commands |
23 | 35 | private const string ConvertSwaggerCommandName = "convert-swagger"; |
24 | 36 |
|
| 37 | + // command options |
25 | 38 | private readonly Option<string> outputDirectoryArg = new("--output-directory", "The output directory for the generated TypeSpec project. This directory must already exist and be empty.") { IsRequired = true }; |
26 | | - |
27 | 39 | private readonly Option<string> swaggerReadmeArg = new("--swagger-readme", "The path or URL to an Azure swagger README file.") { IsRequired = true }; |
28 | 40 | private readonly Option<bool> isArmOption = new("--arm", "Whether the generated TypeSpec project is for an Azure Resource Management (ARM) API. This should be true if the swagger's path contains 'resource-manager'."); |
29 | 41 | private readonly Option<bool> fullyCompatibleOption = new("--fully-compatible", "Whether to generate a TypeSpec project that is fully compatible with the swagger. It is recommended not to set this to true so that the converted TypeSpec project leverages TypeSpec built-in libraries with standard patterns and templates."); |
30 | 42 |
|
31 | 43 | public override Command GetCommand() |
32 | 44 | { |
33 | | - var tspCommand = new Command("tsp", "Tools for initializing TypeSpec projects and converting existing Azure service swagger definitions to TypeSpec projects"); |
34 | 45 |
|
35 | | - var subCommands = new[] |
36 | | - { |
37 | | - new Command(ConvertSwaggerCommandName, "Convert an existing Azure service swagger definition to a TypeSpec project") { |
38 | | - swaggerReadmeArg, |
39 | | - outputDirectoryArg, |
40 | | - isArmOption, |
41 | | - fullyCompatibleOption |
42 | | - } |
| 46 | + Command command = new(ConvertSwaggerCommandName, "Convert an existing Azure service swagger definition to a TypeSpec project") { |
| 47 | + swaggerReadmeArg, |
| 48 | + outputDirectoryArg, |
| 49 | + isArmOption, |
| 50 | + fullyCompatibleOption |
43 | 51 | }; |
| 52 | + command.SetHandler(async ctx => { await HandleCommand(ctx, ctx.GetCancellationToken()); }); |
44 | 53 |
|
45 | | - foreach (var subCommand in subCommands) |
46 | | - { |
47 | | - subCommand.SetHandler(async ctx => { await HandleCommand(ctx, ctx.GetCancellationToken()); }); |
48 | | - tspCommand.AddCommand(subCommand); |
49 | | - } |
50 | | - |
51 | | - return tspCommand; |
| 54 | + return command; |
52 | 55 | } |
53 | 56 |
|
54 | 57 | public override async Task HandleCommand(InvocationContext ctx, CancellationToken ct) |
55 | 58 | { |
56 | | - var command = ctx.ParseResult.CommandResult.Command.Name; |
57 | | - switch (command) |
58 | | - { |
59 | | - case ConvertSwaggerCommandName: |
60 | | - await HandleConvertCommand(ctx, ct); |
61 | | - return; |
62 | | - default: |
63 | | - SetFailure(); |
64 | | - output.Output($"Unknown command: {command}"); |
65 | | - return; |
66 | | - } |
| 59 | + await HandleConvertCommandAsync(ctx, ct); |
67 | 60 | } |
68 | 61 |
|
69 | | - private async Task HandleConvertCommand(InvocationContext ctx, CancellationToken ct) |
| 62 | + private async Task HandleConvertCommandAsync(InvocationContext ctx, CancellationToken ct) |
70 | 63 | { |
71 | 64 | var swaggerReadme = ctx.ParseResult.GetValueForOption(swaggerReadmeArg); |
72 | 65 | var outputDirectory = ctx.ParseResult.GetValueForOption(outputDirectoryArg); |
73 | 66 | var isArm = ctx.ParseResult.GetValueForOption(isArmOption); |
74 | 67 | var fullyCompatible = ctx.ParseResult.GetValueForOption(fullyCompatibleOption); |
75 | 68 |
|
76 | | - TspToolResponse result = await ConvertSwagger(swaggerReadme, outputDirectory, isArm, fullyCompatible, true, ct); |
| 69 | + TspToolResponse result = await ConvertSwaggerAsync(swaggerReadme, outputDirectory, isArm, fullyCompatible, true, ct); |
77 | 70 | ctx.ExitCode = ExitCode; |
78 | 71 | output.Output(result); |
79 | 72 | } |
80 | 73 |
|
81 | | - [McpServerTool(Name = "azsdk_convert_swagger_to_typespec"), Description(@"Converts an existing Azure service swagger definition to a TypeSpec project. |
82 | | - Pass in the `pathToSwaggerReadme` which is the path to the swagger README file. |
83 | | - Pass in the `outputDirectory` where the TypeSpec project should be created. This must be an existing empty directory. |
84 | | - Pass in `isAzureResourceManagement` to indicate whether the swagger is for an Azure Resource Management (ARM) API. |
85 | | - This should be true if the swagger's path contains `resource-manager`. |
86 | | - Pass in `fullyCompatible` to indicate whether the generated TypeSpec project should be fully compatible with the swagger. |
87 | | - It is recommended not to set this to `true` so that the converted TypeSpec project |
88 | | - leverages TypeSpec built-in libraries with standard patterns and templates. |
89 | | - Returns path to the created project.")] |
90 | | - public async Task<TspToolResponse> ConvertSwagger( |
| 74 | + [ |
| 75 | + McpServerTool(Name = "azsdk_convert_swagger_to_typespec"), |
| 76 | + Description("Converts an existing Azure service swagger definition to a TypeSpec project. Returns path to the created project.") |
| 77 | + ] |
| 78 | + public async Task<TspToolResponse> ConvertSwaggerAsync( |
| 79 | + [Description("Path to the swagger README file.")] |
91 | 80 | string pathToSwaggerReadme, |
| 81 | + [Description("The output directory for the generated TypeSpec project. This must be an existing empty directory.")] |
92 | 82 | string outputDirectory, |
| 83 | + [Description(@" |
| 84 | + Indicates whether the swagger is for an Azure Resource Management (ARM) API. |
| 85 | + Should be true if the swagger's path contains `resource-manager`. |
| 86 | + ") |
| 87 | + ] |
93 | 88 | bool? isAzureResourceManagement, |
| 89 | + [Description(@" |
| 90 | + Indicates whether the generated TypeSpec project should be fully compatible with the swagger. |
| 91 | + It is recommended to set this to `false` so that the generated project leverages TypeSpec built-in libraries with standard patterns and templates. |
| 92 | + ") |
| 93 | + ] |
94 | 94 | bool? fullyCompatible, |
95 | 95 | bool isCli, |
96 | 96 | CancellationToken ct |
@@ -121,12 +121,12 @@ CancellationToken ct |
121 | 121 | SetFailure(); |
122 | 122 | return new TspToolResponse |
123 | 123 | { |
124 | | - ResponseError = $"Failed: Invalid --output-dir, {validationResult}" |
| 124 | + ResponseError = $"Failed: Invalid --output-directory, {validationResult}" |
125 | 125 | }; |
126 | 126 | } |
127 | 127 |
|
128 | 128 | var fullOutputDir = Path.GetFullPath(outputDirectory.Trim()); |
129 | | - return await RunTspClient(fullPathToSwaggerReadme, fullOutputDir, isAzureResourceManagement ?? false, fullyCompatible ?? false, isCli, ct); |
| 129 | + return await RunTspClientAsync(fullPathToSwaggerReadme, fullOutputDir, isAzureResourceManagement ?? false, fullyCompatible ?? false, isCli, ct); |
130 | 130 | } |
131 | 131 | catch (Exception ex) |
132 | 132 | { |
@@ -155,14 +155,13 @@ CancellationToken ct |
155 | 155 | var fullPathToSwaggerReadme = Path.GetFullPath(pathToSwaggerReadme.Trim()); |
156 | 156 | if (!File.Exists(fullPathToSwaggerReadme)) |
157 | 157 | { |
158 | | - |
159 | 158 | return $"Failed: pathToSwaggerReadme '{fullPathToSwaggerReadme}' does not exist."; |
160 | 159 | } |
161 | 160 |
|
162 | 161 | return null; // Validation passed |
163 | 162 | } |
164 | 163 |
|
165 | | - private async Task<TspToolResponse> RunTspClient( |
| 164 | + private async Task<TspToolResponse> RunTspClientAsync( |
166 | 165 | string pathToSwaggerReadme, |
167 | 166 | string outputDirectory, |
168 | 167 | bool isAzureResourceManagement, |
|
0 commit comments