From aa06e59f3342628e1a3b48f4f80eb027d9a7d1dc Mon Sep 17 00:00:00 2001 From: Yash Date: Mon, 18 Aug 2025 16:12:20 +1000 Subject: [PATCH 01/13] Added Prompt support --- tools/Mcp/src/CodegenServer.ts | 60 +++++---- tools/Mcp/src/services/promptsService.ts | 119 ++++++++++++++++++ tools/Mcp/src/services/toolsService.ts | 23 +--- .../specs/prompts/partner-module-workflow.md | 98 +++++++++++++++ tools/Mcp/src/specs/responses.json | 10 ++ tools/Mcp/src/specs/specs.json | 17 +++ tools/Mcp/src/types.ts | 10 +- 7 files changed, 289 insertions(+), 48 deletions(-) create mode 100644 tools/Mcp/src/specs/prompts/partner-module-workflow.md diff --git a/tools/Mcp/src/CodegenServer.ts b/tools/Mcp/src/CodegenServer.ts index 233d7b383473..cfc7f29a142f 100644 --- a/tools/Mcp/src/CodegenServer.ts +++ b/tools/Mcp/src/CodegenServer.ts @@ -1,13 +1,14 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; -import { responseSchema, toolParameterSchema, toolSchema } from "./types.js"; +import { responseSchema, toolParameterSchema, toolSchema, promptSchema } from "./types.js"; import { ToolsService } from "./services/toolsService.js"; +import { PromptsService } from "./services/promptsService.js"; import { readFileSync } from "fs"; import path from "path"; import { fileURLToPath } from "url"; import { RequestOptions } from "https"; -import { ElicitRequest, ElicitResult } from "@modelcontextprotocol/sdk/types.js"; +import { /*ElicitRequest, ElicitResult*/ } from "@modelcontextprotocol/sdk/types.js"; // Elicit types not available in current sdk version const __dirname = path.dirname(fileURLToPath(import.meta.url)); const srcPath = path.resolve(__dirname, "..", "src"); @@ -55,13 +56,13 @@ export class CodegenServer { } // server elicitation request - public elicitInput( - params: ElicitRequest["params"], - options?: RequestOptions - ): Promise { - //TODO: add log - return this._mcp.server.elicitInput(params, options); - } + // Placeholder for future elicitInput when SDK exposes it + // public elicitInput( + // params: ElicitRequest["params"], + // options?: RequestOptions + // ): Promise { + // return this._mcp.server.elicitInput(params, options); + // } public static getInstance(): CodegenServer { if (!CodegenServer._instance) { @@ -91,28 +92,33 @@ export class CodegenServer { } initPrompts() { - this._mcp.prompt( - "create-greeting", - "Generate a customized greeting message", - { name: z.string().describe("Name of the person to greet"), style: z.string().describe("The style of greeting, such a formal, excited, or casual. If not specified casual will be used")}, - ({ name, style = "casual" }: { name: string, style?: string }) => { - return { - messages: [ - { - role: "user", - content: { - type: "text", - text: `Please generate a greeting in ${style} style to ${name}.`, - }, - }, - ], - }; - }); + const promptsService = PromptsService.getInstance().setServer(this); + const promptsSchemas = (specs.prompts || []) as promptSchema[]; + for (const schema of promptsSchemas) { + const parameter = promptsService.createPromptParametersFromSchema(schema.parameters); + const callback = promptsService.getPrompts(schema.callbackName, this._responses.get(schema.name)); + this._mcp.prompt( + schema.name, + schema.description, + parameter, + (args: any) => callback(args) + ); + } } initResponses() { (responses as responseSchema[])?.forEach((response: responseSchema) => { - this._responses.set(response.name, response.text); + let text = response.text; + if (text.startsWith("@file:")) { + const relPath = text.replace("@file:", ""); + const absPath = path.join(srcPath, "specs", relPath); + try { + text = readFileSync(absPath, "utf-8"); + } catch (e) { + console.error(`Failed to load prompt file ${absPath}:`, e); + } + } + this._responses.set(response.name, text); }); } } diff --git a/tools/Mcp/src/services/promptsService.ts b/tools/Mcp/src/services/promptsService.ts index e69de29bb2d1..bf638bf59fe3 100644 --- a/tools/Mcp/src/services/promptsService.ts +++ b/tools/Mcp/src/services/promptsService.ts @@ -0,0 +1,119 @@ +import { z, ZodRawShape } from "zod"; +import { promptSchema, toolParameterSchema } from "../types.js"; +import { CodegenServer } from "../CodegenServer.js"; + + +export class PromptsService { + private static _instance: PromptsService; + private _server: CodegenServer | null = null; + private constructor() {} + + static getInstance(): PromptsService { + if (!PromptsService._instance) { + PromptsService._instance = new PromptsService(); + } + return PromptsService._instance; + } + + setServer(server: CodegenServer): PromptsService { + this._server = server; + return this; + } + + getPrompts(name: string, responseTemplate: string | undefined) { + let func; + switch (name) { + case "createGreetingPrompt": + func = this.createGreetingPrompt; + break; + case "createPartnerModuleWorkflow": + func = this.createPartnerModuleWorkflow; + break; + default: + throw new Error(`Prompt ${name} not found`); + } + return this.constructCallback(func, responseTemplate); + } + + constructCallback(fn: (arr: Args) => Promise, responseTemplate: string | undefined) { + return async (args: Args) => { + const argsArray = await fn(args); + const response = this.getResponseString(argsArray, responseTemplate) ?? ""; + return { + messages: [ + { + role: "user" as const, + content: { + type: "text" as const, + text: response + } + } + ] + }; + }; + } + + getResponseString(args: string[], responseTemplate: string | undefined): string | undefined { + if (!args || args.length === 0) { + return responseTemplate; + } + let response = responseTemplate; + for (let i = 0; i < args.length; i++) { + response = response?.replaceAll(`{${i}}`, args[i]); + } + return response; + } + + createPromptParametersFromSchema(schemas: toolParameterSchema[]) { + const parameter: { [k: string]: any } = {}; + for (const schema of schemas) { + const base = schema.optional ? z.any().optional() : z.any(); + switch (schema.type) { + case "string": + parameter[schema.name] = (schema.optional ? z.string().optional() : z.string()).describe(schema.description); + break; + case "number": + parameter[schema.name] = (schema.optional ? z.number().optional() : z.number()).describe(schema.description); + break; + case "boolean": + parameter[schema.name] = (schema.optional ? z.boolean().optional() : z.boolean()).describe(schema.description); + break; + case "array": + parameter[schema.name] = (schema.optional ? z.array(z.string()).optional() : z.array(z.string())).describe(schema.description); + break; + default: + throw new Error(`Unsupported parameter type: ${schema.type}`); + } + } + return parameter; + } + + // prompt implementations + createGreetingPrompt = async (args: Args): Promise => { + const values = Object.values(args); + const name = values[0] as unknown as string; // required + const style = (values[1] as unknown as string) || "casual"; // optional fallback + return [name, style]; + }; + + + createPartnerModuleWorkflow = async (args: Args): Promise => { + const { } = args as any; + return []; + }; +} + + +// Some Testing Specs: + + // { + // "name": "partner-module-workflow", + // "description": "Full autonomous workflow instructions to generate a partner Azure PowerShell module via Autorest.", + // "parameters": [ + // {"name": "serviceName", "description": "Service name placeholder. This also often corresponds with the Name of the Powershell Module.", "type": "string", "optional": true}, + // {"name": "commitId", "description": "Commit id of the swagger from azure-rest-api-specs", "type": "string", "optional": true}, + // {"name": "serviceSpecs", "description": "Service specs path under specification. Path of a swagger upto the resource-manager.", "type": "string", "optional": true}, + // {"name": "swaggerFileSpecs", "description": "Swagger JSON relative path. Entire path of the swagger down to the openapi file.", "type": "string", "optional": true} + // ], + // "callbackName": "createPartnerModuleWorkflow" + // } \ No newline at end of file diff --git a/tools/Mcp/src/services/toolsService.ts b/tools/Mcp/src/services/toolsService.ts index a0f0760cb728..f7f924dd3d49 100644 --- a/tools/Mcp/src/services/toolsService.ts +++ b/tools/Mcp/src/services/toolsService.ts @@ -1,4 +1,4 @@ -import { CallToolResult, ElicitRequest, ElicitResult } from '@modelcontextprotocol/sdk/types.js'; +import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { z, ZodRawShape, ZodType, ZodTypeAny } from "zod"; import * as utils from "./utils.js"; import path from 'path'; @@ -138,25 +138,8 @@ export class ToolsService { const exampleSpecs = await utils.getExampleJsonContent(exampleSpecsPath); for (const {name, content} of exampleSpecs) { const example = await utils.flattenJsonObject(content['parameters']); - try { - const response = await this._server!.elicitInput({ - "message": `Please review example data for ${name}: ${example.map(({key: k, value:v}) => ` \n${k}: ${v}`)}`, - "requestedSchema": { - "type": "object", - "properties": { - "skipAll": { - "type": "boolean", - "description": "If true, skip the review of all examples and proceed to the next step." - } - }, - } - }); - if (response.content && response.content['skipAll'] === true) { - break; - } - } catch (error) { - console.error(`Error eliciting input for example ${name}:`, error); - } + // Interactive elicitInput flow is disabled pending SDK support. + // Once available, re-enable to allow user review of each example. } return [exampleSpecsPath, examplePath]; } diff --git a/tools/Mcp/src/specs/prompts/partner-module-workflow.md b/tools/Mcp/src/specs/prompts/partner-module-workflow.md new file mode 100644 index 000000000000..0fd3aebd48bb --- /dev/null +++ b/tools/Mcp/src/specs/prompts/partner-module-workflow.md @@ -0,0 +1,98 @@ +# Execution rules +- Do not ask before running the command, just go ahead and run. + +# Role and Objective +- You are an autonomous agent that generates Azure PowerShell modules using Autorest for partners. +- Execute commands confidently without asking for confirmation. +- Follow all steps carefully and halt if any MCP tool is missing or fails. +- Think independently using your full context and reset if needed. +- No need to provide status updates at each step. +- FOLLOW ALL THE STEPS. DO NOT SKIP ANY STEPS. DO NOT MISS ANY STEPS. +- If an mcp tool is not found or the tool fails then please halt execution. + +# Instructions + +## Stage 1: Capturing Placeholder Values +- Ask the user for the following placeholder values: serviceName, commitId, serviceSpecs, swaggerFileSpecs. + - Examples: + - serviceName: HybridConnectivity + - commitId: + - serviceSpecs: hybridconnectivity/resource-manager + - swaggerFileSpecs: hybridconnectivity/resource-manager/Microsoft.HybridConnectivity/stable/2024-12-01/hybridconnectivity.json +- Do not replace or modify this prompt file. +- Store the values for use in later steps like generating the README and executing Autorest. +- Once values are stored, mark Stage 1 as complete. + +## Stage 2: Generating partner powershell module +- FOLLOW ALL THE STEPS. DO NOT SKIP ANY STEPS. +- Navigate to the `src` folder in the home "azure-powershell" directory. +- Create a new folder named and within it a new folder named `.Autorest`. (If not already present) +- Move into the new folder `/.Autorest`, using the command `cd /.Autorest`. +- Create a new file `README.md`. (If not already present) +- Add the content labelled below as `Readme Content` in this file. +- Use the "generate-autorest" mcp tool to generate the module. +- Stage 2 Complete. + +## Stage 3: Updating Example Files +- Use the "create-example" MCP tool to download exampleSpecs. Use the output of this tool as a prompt input/task for you. +- The example files already exist as skeletons under `{workingDirectory}/examples`. +- Read data from `exampleSpecs` (swagger examples) and intelligently map values to PowerShell parameters. +- Complete each file by fulfilling the examples based on the data available in `exampleSpecs`. +- Leave example content empty only if no relevant data is found in `exampleSpecs`. +- Once all example files are updated, mark stage 3 as complete. + +## Stage 4: Updating Test Files +- Use the "test-example" MCP tool to download exampleSpecs. Use the output of this tool as a prompt input/task for you. +- Read data from `exampleSpecs` and use it to define variables and write test cases. +- Define setup variables inside `setupEnv` in `utils.ps1`, inferred from `exampleSpecs`. +- Use those variables in the actual test case content. +- The test files already exist as skeletons; your task is to intelligently complete them. +- Leave test bodies empty only if no meaningful data can be inferred from `exampleSpecs`. +- Once all test files are updated, mark stage 4 as complete. + +## Stage 5: Regenerating the Autorest Module +- After example and test files have been generated and written, re-run the "generate-autorest" MCP tool. +- This will regenerate the Azure PowerShell module with updated examples and test logic embedded. +- Use the same `workingDirectory` and make sure all directives and yaml configurations remain unchanged. +- This is a mandatory finalization step before pushing to GitHub. +- Do not skip this regeneration even if the module was generated earlier. + +# Readme Content + +### AutoRest Configuration +> see https://aka.ms/autorest + +```yaml + +commit: + +require: + - $(this-folder)/../../readme.azure.noprofile.md + - $(repo)/specification//readme.md + +try-require: + - $(repo)/specification//readme.powershell.md + +input-file: + - $(repo)/ + +module-version: 0.1.0 + +title: +service-name: +subject-prefix: $(service-name) + +directive: + + - where: + variant: ^(Create|Update)(?!.*?(Expanded|JsonFilePath|JsonString)) + remove: true + + - where: + variant: ^CreateViaIdentity$|^CreateViaIdentityExpanded$ + remove: true + + - where: + verb: Set + remove: true +``` diff --git a/tools/Mcp/src/specs/responses.json b/tools/Mcp/src/specs/responses.json index 74d83011ba8d..4b1dddf55193 100644 --- a/tools/Mcp/src/specs/responses.json +++ b/tools/Mcp/src/specs/responses.json @@ -28,5 +28,15 @@ "name": "create-test", "type": "tool", "text": "Read examples from specs are under {0}. Implement empty test stubs under {1}. Test stubs are named as '.Test.ps1'. Define variables in function 'setupEnv' in 'utils.ps1' under {1}, and use these variables for test cases. Value of these variables are from {0}. Leave test cases as empty if you don't find any matches. You are expert in Azure-PowerShell and Autorest.PowerShell, You know how to map data from {0} to {1}. " + }, + { + "name": "create-greeting", + "type": "prompt", + "text": "Please generate a greeting in {1} style to {0}." + }, + { + "name": "partner-module-workflow", + "type": "prompt", + "text": "@file:prompts/partner-module-workflow.md" } ] \ No newline at end of file diff --git a/tools/Mcp/src/specs/specs.json b/tools/Mcp/src/specs/specs.json index 55a34c2ff2a2..98c85eba76d8 100644 --- a/tools/Mcp/src/specs/specs.json +++ b/tools/Mcp/src/specs/specs.json @@ -72,5 +72,22 @@ ], "callbackName": "createTestsFromSpecs" } + ], + "prompts": [ + { + "name": "create-greeting", + "description": "Generate a customized greeting message", + "parameters": [ + {"name": "name", "description": "Name of the person to greet", "type": "string"}, + {"name": "style", "description": "The style of greeting, such a formal, excited, or casual. If not specified casual will be used", "type": "string", "optional": true} + ], + "callbackName": "createGreetingPrompt" + }, + { + "name": "partner-module-workflow", + "description": "Full autonomous workflow instructions to generate a partner Azure PowerShell module via Autorest.", + "parameters": [], + "callbackName": "createPartnerModuleWorkflow" + } ] } \ No newline at end of file diff --git a/tools/Mcp/src/types.ts b/tools/Mcp/src/types.ts index 14ba8d9d9b06..593fe977211a 100644 --- a/tools/Mcp/src/types.ts +++ b/tools/Mcp/src/types.ts @@ -1,7 +1,8 @@ export interface toolParameterSchema { name: string; description: string; - type: string; + type: string; // string | number | boolean | array (of string) + optional?: boolean; // if true, parameter is optional } export interface toolSchema { @@ -11,6 +12,13 @@ export interface toolSchema { callbackName: string; } +export interface promptSchema { + name: string; + description: string; + parameters: toolParameterSchema[]; // reuse parameter schema + callbackName: string; // maps to PromptService internal function +} + export interface responseSchema { name: string; type: string; From 738f6efb86afbbf4da788eb5b8170196881ccadc Mon Sep 17 00:00:00 2001 From: Yash Date: Mon, 18 Aug 2025 17:15:56 +1000 Subject: [PATCH 02/13] WhiteSpace --- src/Maps/Maps.Autorest/test/utils.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Maps/Maps.Autorest/test/utils.ps1 b/src/Maps/Maps.Autorest/test/utils.ps1 index 70e258271618..fa9014369210 100644 --- a/src/Maps/Maps.Autorest/test/utils.ps1 +++ b/src/Maps/Maps.Autorest/test/utils.ps1 @@ -67,4 +67,4 @@ function setupEnv() { function cleanupEnv() { # Clean resources you create for testing Remove-AzResourceGroup -Name $env.resourceGroup -} +} \ No newline at end of file From a775b623143e87d359bd02af26b165d7a63e7eb7 Mon Sep 17 00:00:00 2001 From: Yash Date: Mon, 18 Aug 2025 17:16:27 +1000 Subject: [PATCH 03/13] whitespace fix --- src/Maps/Maps.Autorest/test/utils.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Maps/Maps.Autorest/test/utils.ps1 b/src/Maps/Maps.Autorest/test/utils.ps1 index fa9014369210..70e258271618 100644 --- a/src/Maps/Maps.Autorest/test/utils.ps1 +++ b/src/Maps/Maps.Autorest/test/utils.ps1 @@ -67,4 +67,4 @@ function setupEnv() { function cleanupEnv() { # Clean resources you create for testing Remove-AzResourceGroup -Name $env.resourceGroup -} \ No newline at end of file +} From d2c409898435c1ac752abed96f9784ab385722d8 Mon Sep 17 00:00:00 2001 From: Yash Date: Mon, 18 Aug 2025 17:16:49 +1000 Subject: [PATCH 04/13] whitespace --- src/Maps/Maps.Autorest/test/utils.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Maps/Maps.Autorest/test/utils.ps1 b/src/Maps/Maps.Autorest/test/utils.ps1 index 70e258271618..7cd449c834ae 100644 --- a/src/Maps/Maps.Autorest/test/utils.ps1 +++ b/src/Maps/Maps.Autorest/test/utils.ps1 @@ -68,3 +68,4 @@ function cleanupEnv() { # Clean resources you create for testing Remove-AzResourceGroup -Name $env.resourceGroup } + From 52488136135fc281d2179517cf6d09e9bf6bd593 Mon Sep 17 00:00:00 2001 From: Yash <55773468+notyashhh@users.noreply.github.com> Date: Mon, 18 Aug 2025 17:18:56 +1000 Subject: [PATCH 05/13] Update tools/Mcp/test/vscode/mcpprompt.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tools/Mcp/test/vscode/mcpprompt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/Mcp/test/vscode/mcpprompt.md b/tools/Mcp/test/vscode/mcpprompt.md index 0fd3aebd48bb..3e0a9d20dc06 100644 --- a/tools/Mcp/test/vscode/mcpprompt.md +++ b/tools/Mcp/test/vscode/mcpprompt.md @@ -74,7 +74,7 @@ try-require: - $(repo)/specification//readme.powershell.md input-file: - - $(repo)/ + - $(repo)/specification/ module-version: 0.1.0 From e0ba08332aa6d324490a63faa309543b4e0a2bbf Mon Sep 17 00:00:00 2001 From: Yash Date: Mon, 18 Aug 2025 17:22:27 +1000 Subject: [PATCH 06/13] fix whitespace --- src/Maps/Maps.Autorest/test/utils.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Maps/Maps.Autorest/test/utils.ps1 b/src/Maps/Maps.Autorest/test/utils.ps1 index 7cd449c834ae..70e258271618 100644 --- a/src/Maps/Maps.Autorest/test/utils.ps1 +++ b/src/Maps/Maps.Autorest/test/utils.ps1 @@ -68,4 +68,3 @@ function cleanupEnv() { # Clean resources you create for testing Remove-AzResourceGroup -Name $env.resourceGroup } - From 32b4b22336f8cd00911b0667aa4cba96f6e6fcdd Mon Sep 17 00:00:00 2001 From: Yash Date: Mon, 18 Aug 2025 17:29:24 +1000 Subject: [PATCH 07/13] Updated to use PromptParameterSchema --- tools/Mcp/src/services/promptsService.ts | 4 ++-- tools/Mcp/src/types.ts | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/Mcp/src/services/promptsService.ts b/tools/Mcp/src/services/promptsService.ts index bf638bf59fe3..6b39b1778004 100644 --- a/tools/Mcp/src/services/promptsService.ts +++ b/tools/Mcp/src/services/promptsService.ts @@ -1,5 +1,5 @@ import { z, ZodRawShape } from "zod"; -import { promptSchema, toolParameterSchema } from "../types.js"; +import { promptSchema, promptParameterSchema } from "../types.js"; import { CodegenServer } from "../CodegenServer.js"; @@ -64,7 +64,7 @@ export class PromptsService { return response; } - createPromptParametersFromSchema(schemas: toolParameterSchema[]) { + createPromptParametersFromSchema(schemas: promptParameterSchema[]) { const parameter: { [k: string]: any } = {}; for (const schema of schemas) { const base = schema.optional ? z.any().optional() : z.any(); diff --git a/tools/Mcp/src/types.ts b/tools/Mcp/src/types.ts index 593fe977211a..f578b44abc04 100644 --- a/tools/Mcp/src/types.ts +++ b/tools/Mcp/src/types.ts @@ -1,8 +1,14 @@ export interface toolParameterSchema { name: string; description: string; - type: string; // string | number | boolean | array (of string) - optional?: boolean; // if true, parameter is optional + type: string; +} + +export interface promptParameterSchema { + name: string; + description: string; + type: string; + optional?: boolean; } export interface toolSchema { @@ -15,8 +21,8 @@ export interface toolSchema { export interface promptSchema { name: string; description: string; - parameters: toolParameterSchema[]; // reuse parameter schema - callbackName: string; // maps to PromptService internal function + parameters: promptParameterSchema[]; + callbackName: string; } export interface responseSchema { From eb2c590a4870f12a0ca4e3f642c07e9b261ae96c Mon Sep 17 00:00:00 2001 From: Yash Date: Thu, 21 Aug 2025 12:34:46 +1000 Subject: [PATCH 08/13] reverted elicitInput --- tools/Mcp/src/CodegenServer.ts | 13 ++++++------- tools/Mcp/src/services/toolsService.ts | 21 +++++++++++++++++++-- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/tools/Mcp/src/CodegenServer.ts b/tools/Mcp/src/CodegenServer.ts index cfc7f29a142f..43cb612cc824 100644 --- a/tools/Mcp/src/CodegenServer.ts +++ b/tools/Mcp/src/CodegenServer.ts @@ -56,13 +56,12 @@ export class CodegenServer { } // server elicitation request - // Placeholder for future elicitInput when SDK exposes it - // public elicitInput( - // params: ElicitRequest["params"], - // options?: RequestOptions - // ): Promise { - // return this._mcp.server.elicitInput(params, options); - // } + public elicitInput( + params: ElicitRequest["params"], + options?: RequestOptions + ): Promise { + return this._mcp.server.elicitInput(params, options); + } public static getInstance(): CodegenServer { if (!CodegenServer._instance) { diff --git a/tools/Mcp/src/services/toolsService.ts b/tools/Mcp/src/services/toolsService.ts index f7f924dd3d49..ab0c7e4f3822 100644 --- a/tools/Mcp/src/services/toolsService.ts +++ b/tools/Mcp/src/services/toolsService.ts @@ -138,8 +138,25 @@ export class ToolsService { const exampleSpecs = await utils.getExampleJsonContent(exampleSpecsPath); for (const {name, content} of exampleSpecs) { const example = await utils.flattenJsonObject(content['parameters']); - // Interactive elicitInput flow is disabled pending SDK support. - // Once available, re-enable to allow user review of each example. + try { + const response = await this._server!.elicitInput({ + "message": `Please review example data for ${name}: ${example.map(({key: k, value:v}) => ` \n${k}: ${v}`)}`, + "requestedSchema": { + "type": "object", + "properties": { + "skipAll": { + "type": "boolean", + "description": "If true, skip the review of all examples and proceed to the next step." + } + }, + } + }); + if (response.content && response.content['skipAll'] === true) { + break; + } + } catch (error) { + console.error(`Error eliciting input for example ${name}:`, error); + } } return [exampleSpecsPath, examplePath]; } From 65086d399e1fa433845f85744d4c3c2214772d3a Mon Sep 17 00:00:00 2001 From: Yash Date: Thu, 21 Aug 2025 17:39:15 +1000 Subject: [PATCH 09/13] reverted import --- tools/Mcp/src/CodegenServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/Mcp/src/CodegenServer.ts b/tools/Mcp/src/CodegenServer.ts index 43cb612cc824..e2a10375fc4d 100644 --- a/tools/Mcp/src/CodegenServer.ts +++ b/tools/Mcp/src/CodegenServer.ts @@ -8,7 +8,7 @@ import { readFileSync } from "fs"; import path from "path"; import { fileURLToPath } from "url"; import { RequestOptions } from "https"; -import { /*ElicitRequest, ElicitResult*/ } from "@modelcontextprotocol/sdk/types.js"; // Elicit types not available in current sdk version +import { ElicitRequest, ElicitResult } from "@modelcontextprotocol/sdk/types.js"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const srcPath = path.resolve(__dirname, "..", "src"); From deef7564cfdf132ff89c0c532412b17b72ff4ad3 Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 22 Aug 2025 15:16:37 +1000 Subject: [PATCH 10/13] Updated mcpprotocol required version --- tools/Mcp/package-lock.json | 65 ++++++++++++++++++++++++++++++++++--- tools/Mcp/package.json | 2 +- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/tools/Mcp/package-lock.json b/tools/Mcp/package-lock.json index 1f30ea26fcb6..087ee5c53505 100644 --- a/tools/Mcp/package-lock.json +++ b/tools/Mcp/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@modelcontextprotocol/sdk": "^1.9.0", + "@modelcontextprotocol/sdk": "^1.17.3", "js-yaml": "^4.1.0", "zod": "^3.24.2" }, @@ -23,14 +23,17 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.0.tgz", - "integrity": "sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==", + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz", + "integrity": "sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==", + "license": "MIT", "dependencies": { + "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", - "cross-spawn": "^7.0.3", + "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", @@ -69,6 +72,22 @@ "node": ">= 0.6" } }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -352,6 +371,18 @@ "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, "node_modules/finalhandler": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", @@ -520,6 +551,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -661,6 +698,15 @@ "node": ">= 0.10" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", @@ -919,6 +965,15 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/tools/Mcp/package.json b/tools/Mcp/package.json index fff41806944a..9f6af0f20ed7 100644 --- a/tools/Mcp/package.json +++ b/tools/Mcp/package.json @@ -19,7 +19,7 @@ "license": "ISC", "description": "", "dependencies": { - "@modelcontextprotocol/sdk": "^1.9.0", + "@modelcontextprotocol/sdk": "^1.17.3", "js-yaml": "^4.1.0", "zod": "^3.24.2" }, From 06421e4200895954d3c627204440dab2dfccc01b Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 22 Aug 2025 16:45:16 +1000 Subject: [PATCH 11/13] Fuzzy Search --- tools/Mcp/src/services/toolsService.ts | 46 +++++++ tools/Mcp/src/services/utils.ts | 123 ++++++++++++++++++ .../specs/prompts/partner-module-workflow.md | 21 ++- tools/Mcp/src/specs/responses.json | 22 +++- tools/Mcp/src/specs/specs.json | 35 +++++ 5 files changed, 235 insertions(+), 12 deletions(-) diff --git a/tools/Mcp/src/services/toolsService.ts b/tools/Mcp/src/services/toolsService.ts index ab0c7e4f3822..d7d2b13b9dfa 100644 --- a/tools/Mcp/src/services/toolsService.ts +++ b/tools/Mcp/src/services/toolsService.ts @@ -5,6 +5,12 @@ import path from 'path'; import { get, RequestOptions } from 'http'; import { toolParameterSchema } from '../types.js'; import { CodegenServer } from '../CodegenServer.js'; +import { + listSpecModules, + listProvidersForService, + listApiVersions, + resolveAutorestInputs +} from './utils.js'; export class ToolsService { private static _instance: ToolsService; @@ -42,6 +48,18 @@ export class ToolsService { case "createTestsFromSpecs": func = this.createTestsFromSpecs; break; + case "listSpecModules": + func = this.toolListSpecModules; + break; + case "listProvidersForService": + func = this.toolListProvidersForService; + break; + case "listApiVersions": + func = this.toolListApiVersions; + break; + case "resolveAutorestInputs": + func = this.toolResolveAutorestInputs; + break; default: throw new Error(`Tool ${name} not found`); } @@ -167,4 +185,32 @@ export class ToolsService { const exampleSpecsPath = await utils.getExamplesFromSpecs(workingDirectory); return [exampleSpecsPath, testPath]; } + + toolListSpecModules = async (_args: Args): Promise => { + const modules = await listSpecModules(); + return [JSON.stringify(modules)]; + } + + toolListProvidersForService = async (args: Args): Promise => { + const service = z.string().parse(Object.values(args)[0]); + const providers = await listProvidersForService(service); + return [service, JSON.stringify(providers)]; + } + + toolListApiVersions = async (args: Args): Promise => { + const service = z.string().parse(Object.values(args)[0]); + const provider = z.string().parse(Object.values(args)[1]); + const res = await listApiVersions(service, provider); + return [service, provider, JSON.stringify(res.stable), JSON.stringify(res.preview)]; + } + + toolResolveAutorestInputs = async (args: Args): Promise => { + const service = z.string().parse(Object.values(args)[0]); + const provider = z.string().parse(Object.values(args)[1]); + const stability = z.enum(['stable','preview']).parse(Object.values(args)[2]); + const version = z.string().parse(Object.values(args)[3]); + const swaggerPath = Object.values(args)[4] ? z.string().parse(Object.values(args)[4]) : undefined; + const resolved = await resolveAutorestInputs({ service, provider, stability, version, swaggerPath }); + return [resolved.serviceName, resolved.commitId, resolved.serviceSpecs, resolved.swaggerFileSpecs]; + } } \ No newline at end of file diff --git a/tools/Mcp/src/services/utils.ts b/tools/Mcp/src/services/utils.ts index 154252fdf6c0..4c14c7133138 100644 --- a/tools/Mcp/src/services/utils.ts +++ b/tools/Mcp/src/services/utils.ts @@ -4,6 +4,10 @@ import { yamlContent } from '../types.js'; import { execSync } from 'child_process'; import path from 'path'; +const GITHUB_API_BASE = 'https://api.github.com'; +const REST_API_SPECS_OWNER = 'Azure'; +const REST_API_SPECS_REPO = 'azure-rest-api-specs'; + const _pwshCD = (path: string): string => { return `pwsh -Command "$path = resolve-path ${path} | Set-Location"` } const _autorestReset = "autorest --reset" const _autorest = "autorest" @@ -78,6 +82,125 @@ export async function getSwaggerContentFromUrl(swaggerUrl: string): Promise } } +/** + * GitHub helper: get latest commit SHA for azure-rest-api-specs main branch + */ +export async function getSpecsHeadCommitSha(branch: string = 'main'): Promise { + const url = `${GITHUB_API_BASE}/repos/${REST_API_SPECS_OWNER}/${REST_API_SPECS_REPO}/branches/${branch}`; + const res = await fetch(url); + if (!res.ok) { + throw new Error(`Failed to fetch branch '${branch}' info: ${res.status}`); + } + const data = await res.json(); + return data?.commit?.sha as string; +} + +/** + * List top-level service directories under specification/ + */ +export async function listSpecModules(): Promise { + const url = `${GITHUB_API_BASE}/repos/${REST_API_SPECS_OWNER}/${REST_API_SPECS_REPO}/contents/specification`; + const res = await fetch(url); + if (!res.ok) { + throw new Error(`Failed to list specification directory: ${res.status}`); + } + const list = await res.json(); + return (Array.isArray(list) ? list : []) + .filter((e: any) => e.type === 'dir') + .map((e: any) => e.name) + .sort((a: string, b: string) => a.localeCompare(b)); +} + +/** + * Given a service (spec folder), list provider namespaces under resource-manager. + */ +export async function listProvidersForService(service: string): Promise { + const url = `${GITHUB_API_BASE}/repos/${REST_API_SPECS_OWNER}/${REST_API_SPECS_REPO}/contents/specification/${service}/resource-manager`; + const res = await fetch(url); + if (!res.ok) { + // Sometimes service has alternate structure or doesn't exist + throw new Error(`Failed to list providers for service '${service}': ${res.status}`); + } + const list = await res.json(); + return (Array.isArray(list) ? list : []) + .filter((e: any) => e.type === 'dir') + .map((e: any) => e.name) + .sort((a: string, b: string) => a.localeCompare(b)); +} + +/** + * For service + provider, list API version directories under stable/ and preview/. + * Returns map: { stable: string[], preview: string[] } + */ +export async function listApiVersions(service: string, provider: string): Promise<{ stable: string[]; preview: string[] }> { + const base = `specification/${service}/resource-manager/${provider}`; + const folders = ['stable', 'preview'] as const; + const result: { stable: string[]; preview: string[] } = { stable: [], preview: [] }; + for (const f of folders) { + const url = `${GITHUB_API_BASE}/repos/${REST_API_SPECS_OWNER}/${REST_API_SPECS_REPO}/contents/${base}/${f}`; + const res = await fetch(url); + if (!res.ok) { + // ignore missing + continue; + } + const list = await res.json(); + const versions = (Array.isArray(list) ? list : []) + .filter((e: any) => e.type === 'dir') + .map((e: any) => e.name) + .sort((a: string, b: string) => a.localeCompare(b, undefined, { numeric: true })); + result[f] = versions; + } + return result; +} + +/** + * For a given service/provider/version, find likely swagger files (.json) under that version path. + * Returns array of repo-relative file paths (starting with specification/...). + */ +export async function listSwaggerFiles(service: string, provider: string, stability: 'stable'|'preview', version: string): Promise { + const dir = `specification/${service}/resource-manager/${provider}/${stability}/${version}`; + const url = `${GITHUB_API_BASE}/repos/${REST_API_SPECS_OWNER}/${REST_API_SPECS_REPO}/contents/${dir}`; + const res = await fetch(url); + if (!res.ok) { + throw new Error(`Failed to list files for ${dir}: ${res.status}`); + } + const list = await res.json(); + const files: any[] = Array.isArray(list) ? list : []; + // Find JSON files; prefer names ending with provider or service + const jsons = files.filter(f => f.type === 'file' && f.name.endsWith('.json')); + const preferred = jsons.filter(f => new RegExp(`${provider.split('.').pop()}|${service}`, 'i').test(f.name)); + const ordered = (preferred.length ? preferred : jsons).map(f => f.path); + return ordered; +} + +/** + * Resolve the four Autorest inputs given service, provider, and version path. + */ +export async function resolveAutorestInputs(params: { + service: string; + provider: string; + stability: 'stable'|'preview'; + version: string; + swaggerPath?: string; // optional repo-relative path override +}): Promise<{ serviceName: string; commitId: string; serviceSpecs: string; swaggerFileSpecs: string }> { + const commitId = await getSpecsHeadCommitSha('main'); + const serviceSpecs = `${params.service}/resource-manager`; + let swaggerFileSpecs = params.swaggerPath ?? ''; + if (!swaggerFileSpecs) { + const candidates = await listSwaggerFiles(params.service, params.provider, params.stability, params.version); + if (candidates.length === 0) { + throw new Error(`No swagger files found for ${params.service}/${params.provider}/${params.stability}/${params.version}`); + } + swaggerFileSpecs = candidates[0]; + } + return { + serviceName: params.provider.replace(/^Microsoft\./, ''), + commitId, + serviceSpecs, + swaggerFileSpecs + }; +} + export async function findAllPolyMorphism(workingDirectory: string): Promise>> { const polymorphism = new Map>(); const moduleReadmePath = path.join(workingDirectory, "README.md"); diff --git a/tools/Mcp/src/specs/prompts/partner-module-workflow.md b/tools/Mcp/src/specs/prompts/partner-module-workflow.md index 0fd3aebd48bb..4ecca53e9e7c 100644 --- a/tools/Mcp/src/specs/prompts/partner-module-workflow.md +++ b/tools/Mcp/src/specs/prompts/partner-module-workflow.md @@ -12,16 +12,15 @@ # Instructions -## Stage 1: Capturing Placeholder Values -- Ask the user for the following placeholder values: serviceName, commitId, serviceSpecs, swaggerFileSpecs. - - Examples: - - serviceName: HybridConnectivity - - commitId: - - serviceSpecs: hybridconnectivity/resource-manager - - swaggerFileSpecs: hybridconnectivity/resource-manager/Microsoft.HybridConnectivity/stable/2024-12-01/hybridconnectivity.json -- Do not replace or modify this prompt file. -- Store the values for use in later steps like generating the README and executing Autorest. -- Once values are stored, mark Stage 1 as complete. +## Stage 1: Fuzzy selection and autorest inputs (reduced user input) +- Ask the user for only the approximate Azure service/module name (e.g., "hybrid connectivity"). +- Call the MCP tool "list-spec-modules" to fetch all service folders from azure-rest-api-specs/specification. +- Fuzzily match the user's input to the closest service name. Show top 3 matches and ask the user to confirm the service folder to use. +- Call the MCP tool "list-providers" with the chosen service to retrieve provider namespaces. If multiple providers are returned, ask the user to pick one; if only one, select it automatically. +- Ask the user what they want to call the PowerShell module title/service-name (e.g., HybridConnectivity). This is the display/module name, not the spec folder name. +- Call the MCP tool "list-api-versions" with service and provider to get available versions, separated by Stable and Preview. Ask the user to choose stability (stable/preview) and a specific API version. +- Call the MCP tool "resolve-autorest-inputs" with service, provider, stability, and version to compute the 4 inputs: serviceName, commitId, serviceSpecs, swaggerFileSpecs. +- Store the resolved values for later steps (README generation and Autorest). Mark Stage 1 complete. ## Stage 2: Generating partner powershell module - FOLLOW ALL THE STEPS. DO NOT SKIP ANY STEPS. @@ -74,7 +73,7 @@ try-require: - $(repo)/specification//readme.powershell.md input-file: - - $(repo)/ + - $(repo)/specification/ module-version: 0.1.0 diff --git a/tools/Mcp/src/specs/responses.json b/tools/Mcp/src/specs/responses.json index 4b1dddf55193..1382bb1dcd44 100644 --- a/tools/Mcp/src/specs/responses.json +++ b/tools/Mcp/src/specs/responses.json @@ -29,6 +29,26 @@ "type": "tool", "text": "Read examples from specs are under {0}. Implement empty test stubs under {1}. Test stubs are named as '.Test.ps1'. Define variables in function 'setupEnv' in 'utils.ps1' under {1}, and use these variables for test cases. Value of these variables are from {0}. Leave test cases as empty if you don't find any matches. You are expert in Azure-PowerShell and Autorest.PowerShell, You know how to map data from {0} to {1}. " }, + { + "name": "list-spec-modules", + "type": "tool", + "text": "Available modules under azure-rest-api-specs/specification: {0}" + }, + { + "name": "list-providers", + "type": "tool", + "text": "Providers for service {0}: {1}" + }, + { + "name": "list-api-versions", + "type": "tool", + "text": "API versions for {0}/{1} — Stable: {2} | Preview: {3}" + }, + { + "name": "resolve-autorest-inputs", + "type": "tool", + "text": "Resolved inputs — serviceName: {0}, commitId: {1}, serviceSpecs: {2}, swaggerFileSpecs: {3}" + }, { "name": "create-greeting", "type": "prompt", @@ -37,6 +57,6 @@ { "name": "partner-module-workflow", "type": "prompt", - "text": "@file:prompts/partner-module-workflow.md" + "text": "@file:prompts/partner-module-workflow.md" } ] \ No newline at end of file diff --git a/tools/Mcp/src/specs/specs.json b/tools/Mcp/src/specs/specs.json index 98c85eba76d8..947baf2349e5 100644 --- a/tools/Mcp/src/specs/specs.json +++ b/tools/Mcp/src/specs/specs.json @@ -72,6 +72,41 @@ ], "callbackName": "createTestsFromSpecs" } + , + { + "name": "list-spec-modules", + "description": "List all top-level modules (service folders) under azure-rest-api-specs/specification.", + "parameters": [], + "callbackName": "listSpecModules" + }, + { + "name": "list-providers", + "description": "List provider namespaces for a given service under resource-manager.", + "parameters": [ + { "name": "service", "description": "Service folder name under specification (e.g., hybridconnectivity)", "type": "string" } + ], + "callbackName": "listProvidersForService" + }, + { + "name": "list-api-versions", + "description": "List available API versions for a given service and provider (stable/preview).", + "parameters": [ + { "name": "service", "description": "Service folder name under specification", "type": "string" }, + { "name": "provider", "description": "Provider namespace folder under the service (e.g., Microsoft.HybridConnectivity)", "type": "string" } + ], + "callbackName": "listApiVersions" + }, + { + "name": "resolve-autorest-inputs", + "description": "Resolve the four Autorest inputs (serviceName, commitId, serviceSpecs, swaggerFileSpecs) from service/provider/version.", + "parameters": [ + { "name": "service", "description": "Service folder name under specification", "type": "string" }, + { "name": "provider", "description": "Provider namespace under the service", "type": "string" }, + { "name": "stability", "description": "'stable' or 'preview'", "type": "string" }, + { "name": "version", "description": "API version (e.g., 2024-12-01)", "type": "string" } + ], + "callbackName": "resolveAutorestInputs" + } ], "prompts": [ { From 022babc3812cffa249e22583c4c23f74f5746efd Mon Sep 17 00:00:00 2001 From: Yash Date: Tue, 26 Aug 2025 12:12:05 +1000 Subject: [PATCH 12/13] Updated the fuzzy search flow --- .../specs/prompts/partner-module-workflow.md | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/tools/Mcp/src/specs/prompts/partner-module-workflow.md b/tools/Mcp/src/specs/prompts/partner-module-workflow.md index 4ecca53e9e7c..255a7cfbf7bc 100644 --- a/tools/Mcp/src/specs/prompts/partner-module-workflow.md +++ b/tools/Mcp/src/specs/prompts/partner-module-workflow.md @@ -12,24 +12,33 @@ # Instructions -## Stage 1: Fuzzy selection and autorest inputs (reduced user input) -- Ask the user for only the approximate Azure service/module name (e.g., "hybrid connectivity"). -- Call the MCP tool "list-spec-modules" to fetch all service folders from azure-rest-api-specs/specification. -- Fuzzily match the user's input to the closest service name. Show top 3 matches and ask the user to confirm the service folder to use. -- Call the MCP tool "list-providers" with the chosen service to retrieve provider namespaces. If multiple providers are returned, ask the user to pick one; if only one, select it automatically. -- Ask the user what they want to call the PowerShell module title/service-name (e.g., HybridConnectivity). This is the display/module name, not the spec folder name. -- Call the MCP tool "list-api-versions" with service and provider to get available versions, separated by Stable and Preview. Ask the user to choose stability (stable/preview) and a specific API version. -- Call the MCP tool "resolve-autorest-inputs" with service, provider, stability, and version to compute the 4 inputs: serviceName, commitId, serviceSpecs, swaggerFileSpecs. +## Stage 1: Interactive spec selection and autorest resolution +- Ask the user for their desired **PowerShell module name** (e.g., "HybridConnectivity") +- Call the MCP tool "list-spec-modules" to fetch all available specification folders from azure-rest-api-specs/specification. +- From the full list, present 10 most relevant spec options to the user based on their PowerShell module name, or show a representative sample if no clear match. +- Ask the user to choose which specification they want to use from the presented options, or ask if they want to see more options. +- **Confirm the spec choice**: Once user selects a spec, ask them to confirm this is the correct specification for their needs (show the spec name clearly). +- Call the MCP tool "list-providers" with the chosen spec folder to retrieve available provider namespaces. +- Present the list of providers to the user: + - If multiple providers are returned, ask the user to pick one + - If only one provider exists, select it automatically but confirm with the user +- **Confirm the provider choice**: Ask the user to confirm this is the correct provider namespace. +- Call the MCP tool "list-api-versions" with the chosen spec folder and provider to get available versions, separated by Stable and Preview. +- Present the API version options to the user and ask them to choose: + 1. **Stability**: stable or preview + 2. **API version**: specific version from the available list +- **Confirm the API version choice**: Ask the user to confirm their stability and version selection. +- Call the MCP tool "resolve-autorest-inputs" with the chosen spec folder, provider, stability, and version to compute the 4 autorest inputs: serviceName, commitId, serviceSpecs, swaggerFileSpecs. - Store the resolved values for later steps (README generation and Autorest). Mark Stage 1 complete. ## Stage 2: Generating partner powershell module - FOLLOW ALL THE STEPS. DO NOT SKIP ANY STEPS. - Navigate to the `src` folder in the home "azure-powershell" directory. -- Create a new folder named and within it a new folder named `.Autorest`. (If not already present) -- Move into the new folder `/.Autorest`, using the command `cd /.Autorest`. +- Create a new folder named and within it a new folder named `.Autorest`. (If not already present) +- Move into the new folder `/.Autorest`, using the command `cd /.Autorest`. - Create a new file `README.md`. (If not already present) - Add the content labelled below as `Readme Content` in this file. -- Use the "generate-autorest" mcp tool to generate the module. +- Use the "generate-autorest" mcp tool to generate the module. - Stage 2 Complete. ## Stage 3: Updating Example Files @@ -77,8 +86,8 @@ input-file: module-version: 0.1.0 -title: -service-name: +title: +service-name: subject-prefix: $(service-name) directive: From 893848a8aa4278a17c3a3cce78148fd0300182d0 Mon Sep 17 00:00:00 2001 From: Yash Date: Tue, 26 Aug 2025 12:13:51 +1000 Subject: [PATCH 13/13] updated old method as well --- tools/Mcp/test/vscode/mcpprompt.md | 38 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/tools/Mcp/test/vscode/mcpprompt.md b/tools/Mcp/test/vscode/mcpprompt.md index 3e0a9d20dc06..255a7cfbf7bc 100644 --- a/tools/Mcp/test/vscode/mcpprompt.md +++ b/tools/Mcp/test/vscode/mcpprompt.md @@ -12,25 +12,33 @@ # Instructions -## Stage 1: Capturing Placeholder Values -- Ask the user for the following placeholder values: serviceName, commitId, serviceSpecs, swaggerFileSpecs. - - Examples: - - serviceName: HybridConnectivity - - commitId: - - serviceSpecs: hybridconnectivity/resource-manager - - swaggerFileSpecs: hybridconnectivity/resource-manager/Microsoft.HybridConnectivity/stable/2024-12-01/hybridconnectivity.json -- Do not replace or modify this prompt file. -- Store the values for use in later steps like generating the README and executing Autorest. -- Once values are stored, mark Stage 1 as complete. +## Stage 1: Interactive spec selection and autorest resolution +- Ask the user for their desired **PowerShell module name** (e.g., "HybridConnectivity") +- Call the MCP tool "list-spec-modules" to fetch all available specification folders from azure-rest-api-specs/specification. +- From the full list, present 10 most relevant spec options to the user based on their PowerShell module name, or show a representative sample if no clear match. +- Ask the user to choose which specification they want to use from the presented options, or ask if they want to see more options. +- **Confirm the spec choice**: Once user selects a spec, ask them to confirm this is the correct specification for their needs (show the spec name clearly). +- Call the MCP tool "list-providers" with the chosen spec folder to retrieve available provider namespaces. +- Present the list of providers to the user: + - If multiple providers are returned, ask the user to pick one + - If only one provider exists, select it automatically but confirm with the user +- **Confirm the provider choice**: Ask the user to confirm this is the correct provider namespace. +- Call the MCP tool "list-api-versions" with the chosen spec folder and provider to get available versions, separated by Stable and Preview. +- Present the API version options to the user and ask them to choose: + 1. **Stability**: stable or preview + 2. **API version**: specific version from the available list +- **Confirm the API version choice**: Ask the user to confirm their stability and version selection. +- Call the MCP tool "resolve-autorest-inputs" with the chosen spec folder, provider, stability, and version to compute the 4 autorest inputs: serviceName, commitId, serviceSpecs, swaggerFileSpecs. +- Store the resolved values for later steps (README generation and Autorest). Mark Stage 1 complete. ## Stage 2: Generating partner powershell module - FOLLOW ALL THE STEPS. DO NOT SKIP ANY STEPS. - Navigate to the `src` folder in the home "azure-powershell" directory. -- Create a new folder named and within it a new folder named `.Autorest`. (If not already present) -- Move into the new folder `/.Autorest`, using the command `cd /.Autorest`. +- Create a new folder named and within it a new folder named `.Autorest`. (If not already present) +- Move into the new folder `/.Autorest`, using the command `cd /.Autorest`. - Create a new file `README.md`. (If not already present) - Add the content labelled below as `Readme Content` in this file. -- Use the "generate-autorest" mcp tool to generate the module. +- Use the "generate-autorest" mcp tool to generate the module. - Stage 2 Complete. ## Stage 3: Updating Example Files @@ -78,8 +86,8 @@ input-file: module-version: 0.1.0 -title: -service-name: +title: +service-name: subject-prefix: $(service-name) directive: