Repository navigation
feat(azure): add the localstack-azure-client tool and Azure lifecycle support #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
f95ae5e
feat(azure): add the Azure emulator tool and Azure lifecycle support
DrisDary 371eb9a
chore(azure): keep internal references out of the repository
DrisDary b4a1dcd
docs(azure): tighten the README's Azure section and the test wording
DrisDary bb11f44
ci(azure): no model evals in CI, fail fast on licence errors, scope t…
DrisDary 7fb62bb
fix(azure): address the review of the Azure tool
DrisDary 8d6ba79
ci(azure): run the Azure jobs with the LOCALSTACK_AUTH_TOKEN_AZURE se…
DrisDary 67dd802
fix(azure): bind-mount the emulator's state folder so app deployments…
DrisDary 89ed16a
ci: run every job with the LOCALSTACK_AUTH_TOKEN_AZURE secret
DrisDary 1f12bf3
ci: run every job with the one LOCALSTACK_AUTH_TOKEN secret
DrisDary 73910a8
fix(deps): bump @grpc/grpc-js to 1.14.5 for GHSA-m9gg-hp2v-232j
DrisDary f73e70a
test: skip the docs checks while the docs search service is down
DrisDary 6669013
fix(azure): keep only az command words in the analytics command path
DrisDary f1cdbd4
ci: back to main's workflows
DrisDary cbadcf5
refactor(azure): cut the Azure client to the tool, service: azure and…
DrisDary 78fc944
docs: the Azure tool's setup and settings
DrisDary 4ad89e1
fix(azure): address the second review of the Azure tool
DrisDary ee807d9
Merge remote-tracking branch 'origin/main' into feat/azure-client
DrisDary 9e59784
fix(azure): address the third review of the Azure tool
DrisDary edd0136
docs(azure): move the Azure tool section to docs/AZURE.md
DrisDary File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| name: Azure smoke | ||
|
|
||
| # The Azure tool against the LocalStack Azure emulator, weekly and on demand. | ||
| on: | ||
| workflow_dispatch: | ||
| schedule: | ||
| - cron: "0 6 * * 1" | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| azure-smoke: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| env: | ||
| # The token's licence must include LocalStack for Azure, or the emulator refuses to start. | ||
| LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }} | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Use Node.js 22 | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22.x | ||
|
|
||
| - name: Install dependencies | ||
| run: yarn | ||
|
|
||
| # ubuntu-latest ships the Azure CLI. | ||
| - name: Show the Azure CLI version | ||
| run: az version | ||
|
|
||
| - name: Pull the LocalStack Azure emulator image | ||
| run: docker pull localstack/localstack-azure:latest | ||
|
|
||
| - name: Run az commands through the Azure tool | ||
| run: yarn build && npx playwright test -c playwright.config.mjs tests/mcp/azure-smoke.spec.mjs | ||
|
|
||
| - name: Keep the test results of a failed run | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: azure-smoke-results | ||
| path: .mcp-test-results | ||
| if-no-files-found: ignore | ||
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| hello from the LocalStack MCP server Azure stage |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Using the Azure tool | ||
|
|
||
| `localstack-azure-client` runs the Azure CLI (`az`) installed on your machine against the LocalStack for Azure emulator; start the emulator with `localstack-management` (`service: azure`). Its settings are listed in the [README](../README.md#localstack-configuration). | ||
|
|
||
| - The tool keeps its own Azure CLI profile (`LOCALSTACK_AZ_CONFIG_DIR`), logged in to the emulator with a dummy account, so your own Azure login is never used or changed. | ||
| - It uses your Azure CLI extensions: install one with `az extension add --name <extension>`. | ||
| - Bicep templates need the `bicep` binary on your `PATH` (for example `brew install bicep` or `winget install Microsoft.Bicep`). | ||
| - Relative file paths in commands resolve against `LOCALSTACK_AZ_WORKDIR`, by default the directory the MCP client starts the server in. | ||
| - `rest` calls the emulator: give it a relative URL. An absolute URL must name a local host; a `management.azure.com` URL is turned into a relative one. | ||
| - Only `rest` URLs are checked: an endpoint given in a command, such as `--blob-endpoint` or a connection string, is used as it is. | ||
| - The Docker image does not include the Azure CLI yet: run the server with npx or from source to use the Azure tool. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| /** | ||
| * Quote-aware splitting of an `az` command string into an argv array. No shell is ever involved: | ||
| * the result goes to spawn as an argument vector, so this reproduces POSIX word splitting for a | ||
| * safe subset and refuses anything that would need a shell (pipes, chaining, redirects, | ||
| * substitutions). | ||
| * | ||
| * It is the AWS client's splitter with three changes that `az` commands need (JMESPath queries, | ||
| * empty values, JSON in double quotes): | ||
| * - newline, CR and backtick are refused only outside quotes; inside quotes they are data; | ||
| * - `""` and `''` produce an empty argument instead of disappearing; | ||
| * - inside double quotes, `\$`, `` \` `` and `\<newline>` behave as in bash. | ||
| * | ||
| * As there, an unquoted backslash is literal (Windows paths work), `$VAR` is passed literally, and | ||
| * an unquoted `#word` is an argument. | ||
| */ | ||
|
|
||
| export class AzSyntaxError extends Error { | ||
| constructor(message: string) { | ||
| super(message); | ||
| this.name = "AzSyntaxError"; | ||
| } | ||
| } | ||
|
|
||
| export function splitAzArgs(command: string): string[] { | ||
| const args: string[] = []; | ||
| let current = ""; | ||
| // True once the current word has started, even if it holds no characters yet (""). | ||
| let wordStarted = false; | ||
| let quote: "'" | '"' | undefined; | ||
|
|
||
| const endWord = () => { | ||
| if (wordStarted) args.push(current); | ||
| current = ""; | ||
| wordStarted = false; | ||
| }; | ||
|
|
||
| for (let index = 0; index < command.length; index++) { | ||
| const character = command[index]; | ||
|
|
||
| if (!quote && (character === "\n" || character === "\r" || character === "`")) { | ||
| throw new AzSyntaxError("Command contains forbidden shell syntax."); | ||
| } | ||
|
|
||
| if (character === "\\" && quote === '"') { | ||
| const escaped = command[index + 1]; | ||
| if (escaped === '"' || escaped === "\\" || escaped === "$" || escaped === "`") { | ||
| current += escaped; | ||
| index++; | ||
| } else if (escaped === "\n") { | ||
| index++; // a line continuation: bash drops both characters | ||
| } else { | ||
| current += character; | ||
| } | ||
| continue; | ||
| } | ||
|
|
||
| if ((character === "'" || character === '"') && (!quote || quote === character)) { | ||
| quote = quote ? undefined : character; | ||
| wordStarted = true; | ||
| continue; | ||
| } | ||
|
|
||
| if (!quote) { | ||
| if ( | ||
| ";&|<>".includes(character) || | ||
| command.startsWith("$(", index) || | ||
| command.startsWith("${", index) | ||
| ) { | ||
| throw new AzSyntaxError("Command contains forbidden shell syntax."); | ||
| } | ||
| if (/\s/.test(character)) { | ||
| endWord(); | ||
| continue; | ||
| } | ||
| } | ||
|
|
||
| current += character; | ||
| wordStarted = true; | ||
| } | ||
|
|
||
| if (quote) throw new AzSyntaxError("Command contains an unterminated quote."); | ||
| endWord(); | ||
| return args; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.