@dockndevai/mcp-azure 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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ankit Verma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # mcp-azure
2
+
3
+ [![CI](https://github.com/dockndevai/mcp-azure/actions/workflows/ci.yml/badge.svg)](https://github.com/dockndevai/mcp-azure/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
5
+ [![npm](https://img.shields.io/npm/v/@dockndevai/mcp-azure)](https://www.npmjs.com/package/@dockndevai/mcp-azure)
6
+
7
+ A [Model Context Protocol](https://modelcontextprotocol.io) server for **Azure** (via the Azure Resource Manager API — the programmatic layer behind the Azure Portal). It lets an MCP-capable client (Claude Desktop, Claude Code, Cursor, Codex, …) inventory and operate Azure resources — with a governance layer that keeps an AI agent inside safe boundaries.
8
+
9
+ ## What this offers
10
+
11
+ - **Inventory** — list subscriptions, locations, resource groups, and resources; get any resource by ARM id.
12
+ - **Operations** — create resource groups (in approved regions), merge tags onto any resource, and control VM power state (start / stop / restart / deallocate).
13
+ - **Lifecycle** — delete resource groups and individual resources, guarded.
14
+ - **Governance built in** — access modes, subscription/resource-group allowlists, protected resource groups, a location allowlist for new groups, delete gating, typed confirmation for high-impact deletes, dry-run, and JSON audit logging.
15
+
16
+ ## Governance & security model
17
+
18
+ | Concern | Flag | Default | Effect |
19
+ | --- | --- | --- | --- |
20
+ | What can the server do? | `AZURE_MODE` | `read-only` | `read-only` → inventory; `read-write` → create RG, tag, VM power; `admin` → deletes. Tools above the mode are **never registered**. |
21
+ | Which subscriptions? | `AZURE_SUBSCRIPTION_ALLOWLIST` | *(all)* | Operations on other subscriptions are refused. |
22
+ | Which resource groups? | `AZURE_RESOURCE_GROUP_ALLOWLIST` | *(all)* | Operations outside the list are refused. |
23
+ | Read-only-forever groups | `AZURE_PROTECTED_RESOURCE_GROUPS` | *(none)* | Readable, never mutable. |
24
+ | Approved regions | `AZURE_LOCATION_ALLOWLIST` | *(any)* | New resource groups may only be created here. |
25
+ | Can it delete? | `AZURE_ALLOW_DELETE` | `false` | Deletes need this **and** admin mode. |
26
+ | Typed confirmation | `AZURE_REQUIRE_CONFIRMATION` | `true` | Deletes require `confirm` to equal the target name — not just a boolean. |
27
+ | Preview | `AZURE_DRY_RUN` | `false` | Write/admin tools validate + log intent, then return. |
28
+ | Audit trail | `AZURE_AUDIT_LOG` | `true` | JSON line to stderr per guarded operation. |
29
+
30
+ ## Tools
31
+
32
+ **Read** (`read-only`+): `list_subscriptions`, `list_locations`, `list_resource_groups`, `list_resources`, `get_resource`
33
+
34
+ **Write** (`read-write`+): `create_resource_group`, `tag_resource`, `control_vm`
35
+
36
+ **Admin** (`admin`): `delete_resource_group`, `delete_resource` (both need `AZURE_ALLOW_DELETE` + typed `confirm`)
37
+
38
+ ## Quickstart — add to your agent
39
+
40
+ Published on npm as [`@dockndevai/mcp-azure`](https://www.npmjs.com/package/@dockndevai/mcp-azure). Runs via `npx` with an Entra ID service principal. See [docs/CLIENTS.md](docs/CLIENTS.md) for every client and [`.env.example`](.env.example) for all variables.
41
+
42
+ **Claude Code**
43
+
44
+ ```bash
45
+ claude mcp add azure -e AZURE_TENANT_ID="…" -e AZURE_CLIENT_ID="…" -e AZURE_CLIENT_SECRET="…" -e AZURE_SUBSCRIPTION_ID="…" -e AZURE_MODE="read-only" -- npx -y @dockndevai/mcp-azure
46
+ ```
47
+
48
+ **Claude Desktop · Cursor · Windsurf**
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "azure": {
54
+ "command": "npx",
55
+ "args": ["-y", "@dockndevai/mcp-azure"],
56
+ "env": {
57
+ "AZURE_TENANT_ID": "…",
58
+ "AZURE_CLIENT_ID": "…",
59
+ "AZURE_CLIENT_SECRET": "…",
60
+ "AZURE_SUBSCRIPTION_ID": "…",
61
+ "AZURE_MODE": "read-only"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ ## Example prompts
69
+
70
+ - *"List all resource groups in my subscription and which region each is in"*
71
+ - *"Show every resource in the rg-web group"*
72
+ - *"Tag the app-plan resource with env=prod and owner=team-a"* (needs read-write)
73
+ - *"Stop the build-agent VM in rg-ci"* (needs read-write)
74
+
75
+ ## Run from source (development)
76
+
77
+ ```bash
78
+ npm install
79
+ npm run build
80
+ node dist/index.js # with the environment variables set
81
+ ```
82
+
83
+ ## Develop
84
+
85
+ ```bash
86
+ npm run dev
87
+ npm test # governance policy: modes, scoping, location allowlist, delete + confirmation
88
+ npm run typecheck
89
+ ```
90
+
91
+ ## Publishing
92
+
93
+ Ships a [`server.json`](server.json) for the official MCP registry and an [`mcpName`](package.json) for npm ownership validation. See **[PUBLISHING.md](PUBLISHING.md)**.
94
+
95
+ ## License
96
+
97
+ MIT
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Thin fetch-based client for the Azure Resource Manager (ARM) REST API.
3
+ * Auth is OAuth2 client credentials against Entra ID; tokens are cached.
4
+ * https://learn.microsoft.com/rest/api/resources/
5
+ */
6
+ import type { AzureConnection } from "../config.js";
7
+ export declare class AzureError extends Error {
8
+ readonly status: number;
9
+ readonly body?: string | undefined;
10
+ constructor(message: string, status: number, body?: string | undefined);
11
+ }
12
+ export declare class AzureClient {
13
+ private readonly conn;
14
+ private token?;
15
+ constructor(conn: AzureConnection);
16
+ private getToken;
17
+ request<T = unknown>(method: string, path: string, opts?: {
18
+ apiVersion: string;
19
+ query?: Record<string, string | undefined>;
20
+ body?: unknown;
21
+ }): Promise<T>;
22
+ listSubscriptions(): Promise<unknown>;
23
+ listLocations(sub: string): Promise<unknown>;
24
+ listResourceGroups(sub: string): Promise<unknown>;
25
+ listResources(sub: string, rg?: string): Promise<unknown>;
26
+ getResource(resourceId: string, apiVersion: string): Promise<unknown>;
27
+ createResourceGroup(sub: string, name: string, location: string, tags?: Record<string, string>): Promise<unknown>;
28
+ /** Merge tags onto any resource via the generic Tags API (no per-type api-version needed). */
29
+ tagResource(resourceId: string, tags: Record<string, string>): Promise<unknown>;
30
+ /** Power action on a VM: start | powerOff | restart | deallocate. */
31
+ controlVm(sub: string, rg: string, vm: string, action: string): Promise<unknown>;
32
+ deleteResourceGroup(sub: string, name: string): Promise<unknown>;
33
+ deleteResource(resourceId: string, apiVersion: string): Promise<unknown>;
34
+ }
@@ -0,0 +1,115 @@
1
+ const ARM = "https://management.azure.com";
2
+ export class AzureError extends Error {
3
+ status;
4
+ body;
5
+ constructor(message, status, body) {
6
+ super(message);
7
+ this.status = status;
8
+ this.body = body;
9
+ this.name = "AzureError";
10
+ }
11
+ }
12
+ export class AzureClient {
13
+ conn;
14
+ token;
15
+ constructor(conn) {
16
+ this.conn = conn;
17
+ }
18
+ async getToken() {
19
+ if (this.token && this.token.expiresAt > Date.now())
20
+ return this.token.accessToken;
21
+ const url = `https://login.microsoftonline.com/${this.conn.tenantId}/oauth2/v2.0/token`;
22
+ const form = new URLSearchParams({
23
+ grant_type: "client_credentials",
24
+ client_id: this.conn.clientId,
25
+ client_secret: this.conn.clientSecret,
26
+ scope: `${ARM}/.default`,
27
+ });
28
+ const res = await fetch(url, {
29
+ method: "POST",
30
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
31
+ body: form.toString(),
32
+ });
33
+ if (!res.ok) {
34
+ const body = await res.text().catch(() => "");
35
+ throw new AzureError(`Azure AD token request failed (${res.status}). Check tenant/client/secret.`, res.status, body);
36
+ }
37
+ const data = (await res.json());
38
+ this.token = { accessToken: data.access_token, expiresAt: Date.now() + (data.expires_in - 30) * 1000 };
39
+ return this.token.accessToken;
40
+ }
41
+ async request(method, path, opts = { apiVersion: "2021-04-01" }) {
42
+ const token = await this.getToken();
43
+ const url = new URL(path.startsWith("http") ? path : `${ARM}${path}`);
44
+ url.searchParams.set("api-version", opts.apiVersion);
45
+ for (const [k, v] of Object.entries(opts.query ?? {}))
46
+ if (v !== undefined)
47
+ url.searchParams.set(k, v);
48
+ const ctrl = new AbortController();
49
+ const timer = setTimeout(() => ctrl.abort(), this.conn.requestTimeout);
50
+ try {
51
+ const res = await fetch(url, {
52
+ method,
53
+ headers: {
54
+ Authorization: `Bearer ${token}`,
55
+ Accept: "application/json",
56
+ ...(opts.body !== undefined ? { "Content-Type": "application/json" } : {}),
57
+ },
58
+ body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
59
+ signal: ctrl.signal,
60
+ });
61
+ const text = await res.text();
62
+ if (!res.ok)
63
+ throw new AzureError(`Azure ARM ${method} ${path} failed with ${res.status}.`, res.status, text);
64
+ return (text ? JSON.parse(text) : undefined);
65
+ }
66
+ finally {
67
+ clearTimeout(timer);
68
+ }
69
+ }
70
+ // --- Reads -----------------------------------------------------------------
71
+ listSubscriptions() {
72
+ return this.request("GET", "/subscriptions", { apiVersion: "2022-12-01" });
73
+ }
74
+ listLocations(sub) {
75
+ return this.request("GET", `/subscriptions/${sub}/locations`, { apiVersion: "2022-12-01" });
76
+ }
77
+ listResourceGroups(sub) {
78
+ return this.request("GET", `/subscriptions/${sub}/resourcegroups`, { apiVersion: "2021-04-01" });
79
+ }
80
+ listResources(sub, rg) {
81
+ const path = rg
82
+ ? `/subscriptions/${sub}/resourceGroups/${rg}/resources`
83
+ : `/subscriptions/${sub}/resources`;
84
+ return this.request("GET", path, { apiVersion: "2021-04-01" });
85
+ }
86
+ getResource(resourceId, apiVersion) {
87
+ return this.request("GET", resourceId, { apiVersion });
88
+ }
89
+ // --- Writes ----------------------------------------------------------------
90
+ createResourceGroup(sub, name, location, tags) {
91
+ return this.request("PUT", `/subscriptions/${sub}/resourcegroups/${name}`, {
92
+ apiVersion: "2021-04-01",
93
+ body: { location, tags },
94
+ });
95
+ }
96
+ /** Merge tags onto any resource via the generic Tags API (no per-type api-version needed). */
97
+ tagResource(resourceId, tags) {
98
+ return this.request("PATCH", `${resourceId}/providers/Microsoft.Resources/tags/default`, {
99
+ apiVersion: "2021-04-01",
100
+ body: { operation: "Merge", properties: { tags } },
101
+ });
102
+ }
103
+ /** Power action on a VM: start | powerOff | restart | deallocate. */
104
+ controlVm(sub, rg, vm, action) {
105
+ return this.request("POST", `/subscriptions/${sub}/resourceGroups/${rg}/providers/Microsoft.Compute/virtualMachines/${vm}/${action}`, { apiVersion: "2023-07-01" });
106
+ }
107
+ // --- Destructive -----------------------------------------------------------
108
+ deleteResourceGroup(sub, name) {
109
+ return this.request("DELETE", `/subscriptions/${sub}/resourcegroups/${name}`, { apiVersion: "2021-04-01" });
110
+ }
111
+ deleteResource(resourceId, apiVersion) {
112
+ return this.request("DELETE", resourceId, { apiVersion });
113
+ }
114
+ }
115
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/azure/client.ts"],"names":[],"mappings":"AAOA,MAAM,GAAG,GAAG,8BAA8B,CAAC;AAE3C,MAAM,OAAO,UAAW,SAAQ,KAAK;IACG;IAAyB;IAA/D,YAAY,OAAe,EAAW,MAAc,EAAW,IAAa;QAC1E,KAAK,CAAC,OAAO,CAAC,CAAC;QADqB,WAAM,GAAN,MAAM,CAAQ;QAAW,SAAI,GAAJ,IAAI,CAAS;QAE1E,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAOD,MAAM,OAAO,WAAW;IAGO;IAFrB,KAAK,CAAS;IAEtB,YAA6B,IAAqB;QAArB,SAAI,GAAJ,IAAI,CAAiB;IAAG,CAAC;IAE9C,KAAK,CAAC,QAAQ;QACpB,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;QACnF,MAAM,GAAG,GAAG,qCAAqC,IAAI,CAAC,IAAI,CAAC,QAAQ,oBAAoB,CAAC;QACxF,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC;YAC/B,UAAU,EAAE,oBAAoB;YAChC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ;YAC7B,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY;YACrC,KAAK,EAAE,GAAG,GAAG,WAAW;SACzB,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;YAChE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;SACtB,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,UAAU,CAAC,kCAAkC,GAAG,CAAC,MAAM,gCAAgC,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACvH,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAiD,CAAC;QAChF,IAAI,CAAC,KAAK,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC;QACvG,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,OAAO,CACX,MAAc,EACd,IAAY,EACZ,OAA2F,EAAE,UAAU,EAAE,YAAY,EAAE;QAEvH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC;QACtE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACrD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAAE,IAAI,CAAC,KAAK,SAAS;gBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAEvG,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACvE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC3B,MAAM;gBACN,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,KAAK,EAAE;oBAChC,MAAM,EAAE,kBAAkB;oBAC1B,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC3E;gBACD,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;gBACrE,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,MAAM,IAAI,UAAU,CAAC,aAAa,MAAM,IAAI,IAAI,gBAAgB,GAAG,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC9G,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAM,CAAC;QACpD,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,iBAAiB;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,gBAAgB,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,aAAa,CAAC,GAAW;QACvB,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,GAAG,YAAY,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,kBAAkB,CAAC,GAAW;QAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,GAAG,iBAAiB,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;IACnG,CAAC;IACD,aAAa,CAAC,GAAW,EAAE,EAAW;QACpC,MAAM,IAAI,GAAG,EAAE;YACb,CAAC,CAAC,kBAAkB,GAAG,mBAAmB,EAAE,YAAY;YACxD,CAAC,CAAC,kBAAkB,GAAG,YAAY,CAAC;QACtC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,WAAW,CAAC,UAAkB,EAAE,UAAkB;QAChD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,8EAA8E;IAC9E,mBAAmB,CAAC,GAAW,EAAE,IAAY,EAAE,QAAgB,EAAE,IAA6B;QAC5F,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,GAAG,mBAAmB,IAAI,EAAE,EAAE;YACzE,UAAU,EAAE,YAAY;YACxB,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;SACzB,CAAC,CAAC;IACL,CAAC;IACD,8FAA8F;IAC9F,WAAW,CAAC,UAAkB,EAAE,IAA4B;QAC1D,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,UAAU,6CAA6C,EAAE;YACvF,UAAU,EAAE,YAAY;YACxB,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE;SACnD,CAAC,CAAC;IACL,CAAC;IACD,qEAAqE;IACrE,SAAS,CAAC,GAAW,EAAE,EAAU,EAAE,EAAU,EAAE,MAAc;QAC3D,OAAO,IAAI,CAAC,OAAO,CACjB,MAAM,EACN,kBAAkB,GAAG,mBAAmB,EAAE,gDAAgD,EAAE,IAAI,MAAM,EAAE,EACxG,EAAE,UAAU,EAAE,YAAY,EAAE,CAC7B,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,mBAAmB,CAAC,GAAW,EAAE,IAAY;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,kBAAkB,GAAG,mBAAmB,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;IAC9G,CAAC;IACD,cAAc,CAAC,UAAkB,EAAE,UAAkB;QACnD,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IAC5D,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ import type { SecurityConfig } from "./security.js";
2
+ export interface AzureConnection {
3
+ tenantId: string;
4
+ clientId: string;
5
+ clientSecret: string;
6
+ /** Default subscription id used when a tool omits one. */
7
+ defaultSubscription?: string;
8
+ requestTimeout: number;
9
+ }
10
+ export interface AppConfig {
11
+ connection: AzureConnection;
12
+ security: SecurityConfig;
13
+ }
14
+ export declare function loadConfig(): AppConfig;
package/dist/config.js ADDED
@@ -0,0 +1,47 @@
1
+ function bool(name, fallback) {
2
+ const v = process.env[name];
3
+ if (v === undefined || v === "")
4
+ return fallback;
5
+ return ["1", "true", "yes", "on"].includes(v.toLowerCase());
6
+ }
7
+ function list(name) {
8
+ const v = process.env[name];
9
+ if (!v)
10
+ return [];
11
+ return v.split(",").map((s) => s.trim()).filter(Boolean);
12
+ }
13
+ function parseMode() {
14
+ const raw = (process.env.AZURE_MODE ?? "read-only").toLowerCase();
15
+ if (raw === "read-only" || raw === "read-write" || raw === "admin")
16
+ return raw;
17
+ throw new Error(`Invalid AZURE_MODE '${raw}'. Expected one of: read-only, read-write, admin.`);
18
+ }
19
+ function required(name) {
20
+ const v = process.env[name];
21
+ if (!v)
22
+ throw new Error(`Missing required environment variable: ${name}`);
23
+ return v;
24
+ }
25
+ export function loadConfig() {
26
+ return {
27
+ connection: {
28
+ tenantId: required("AZURE_TENANT_ID"),
29
+ clientId: required("AZURE_CLIENT_ID"),
30
+ clientSecret: required("AZURE_CLIENT_SECRET"),
31
+ defaultSubscription: process.env.AZURE_SUBSCRIPTION_ID || undefined,
32
+ requestTimeout: Number(process.env.AZURE_TIMEOUT_MS ?? 30000),
33
+ },
34
+ security: {
35
+ mode: parseMode(),
36
+ subscriptionAllowlist: list("AZURE_SUBSCRIPTION_ALLOWLIST"),
37
+ resourceGroupAllowlist: list("AZURE_RESOURCE_GROUP_ALLOWLIST"),
38
+ protectedResourceGroups: list("AZURE_PROTECTED_RESOURCE_GROUPS"),
39
+ locationAllowlist: list("AZURE_LOCATION_ALLOWLIST").map((l) => l.toLowerCase().replace(/\s+/g, "")),
40
+ allowDelete: bool("AZURE_ALLOW_DELETE", false),
41
+ requireConfirmation: bool("AZURE_REQUIRE_CONFIRMATION", true),
42
+ dryRun: bool("AZURE_DRY_RUN", false),
43
+ auditLog: bool("AZURE_AUDIT_LOG", true),
44
+ },
45
+ };
46
+ }
47
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAgBA,SAAS,IAAI,CAAC,IAAY,EAAE,QAAiB;IAC3C,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IACjD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,IAAI,CAAC,IAAY;IACxB,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IAClE,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,OAAO;QAAE,OAAO,GAAG,CAAC;IAC/E,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,mDAAmD,CAAC,CAAC;AACjG,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,EAAE,CAAC,CAAC;IAC1E,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,UAAU,EAAE;YACV,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC;YACrC,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC;YACrC,YAAY,EAAE,QAAQ,CAAC,qBAAqB,CAAC;YAC7C,mBAAmB,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,SAAS;YACnE,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,KAAK,CAAC;SAC9D;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,SAAS,EAAE;YACjB,qBAAqB,EAAE,IAAI,CAAC,8BAA8B,CAAC;YAC3D,sBAAsB,EAAE,IAAI,CAAC,gCAAgC,CAAC;YAC9D,uBAAuB,EAAE,IAAI,CAAC,iCAAiC,CAAC;YAChE,iBAAiB,EAAE,IAAI,CAAC,0BAA0B,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACnG,WAAW,EAAE,IAAI,CAAC,oBAAoB,EAAE,KAAK,CAAC;YAC9C,mBAAmB,EAAE,IAAI,CAAC,4BAA4B,EAAE,IAAI,CAAC;YAC7D,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,KAAK,CAAC;YACpC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC;SACxC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { loadConfig } from "./config.js";
4
+ import { buildServer } from "./server.js";
5
+ async function main() {
6
+ let config;
7
+ try {
8
+ config = loadConfig();
9
+ }
10
+ catch (err) {
11
+ process.stderr.write(`[azure-mcp] Configuration error: ${err.message}\n`);
12
+ process.exit(1);
13
+ }
14
+ const { server, enabled } = buildServer(config);
15
+ process.stderr.write(`[azure-mcp] Starting in '${config.security.mode}' mode` +
16
+ `${config.security.dryRun ? " (DRY RUN)" : ""}. ` +
17
+ `${enabled.length} tools enabled: ${enabled.join(", ")}\n`);
18
+ const transport = new StdioServerTransport();
19
+ await server.connect(transport);
20
+ }
21
+ main().catch((err) => {
22
+ process.stderr.write(`[azure-mcp] Fatal: ${err.stack ?? err}\n`);
23
+ process.exit(1);
24
+ });
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,KAAK,UAAU,IAAI;IACjB,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,UAAU,EAAE,CAAC;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAqC,GAAa,CAAC,OAAO,IAAI,CAAC,CAAC;QACrF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAChD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,4BAA4B,MAAM,CAAC,QAAQ,CAAC,IAAI,QAAQ;QACtD,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,IAAI;QACjD,GAAG,OAAO,CAAC,MAAM,mBAAmB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAC7D,CAAC;IACF,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAuB,GAAa,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;IAC5E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Governance & security policy engine for Azure Resource Manager.
3
+ *
4
+ * Shared model (access modes + capability gating + delete gating + dry-run +
5
+ * audit) plus Azure governance controls:
6
+ * - Subscription and resource-group allowlists.
7
+ * - Protected resource groups: readable, never mutated or deleted.
8
+ * - Location allowlist: new resource groups may only be created in approved regions.
9
+ * - High-impact confirmation: destructive ops must echo back the exact target name.
10
+ *
11
+ * Pure logic, no I/O — fully unit-testable.
12
+ */
13
+ export type Capability = "read" | "write" | "admin";
14
+ export type AccessMode = "read-only" | "read-write" | "admin";
15
+ export interface SecurityConfig {
16
+ mode: AccessMode;
17
+ /** If set, only these subscription ids are in scope. Empty = all. */
18
+ subscriptionAllowlist: string[];
19
+ /** If set, only these resource groups may be touched. Empty = all. */
20
+ resourceGroupAllowlist: string[];
21
+ /** Resource groups that can be read but never mutated or deleted. */
22
+ protectedResourceGroups: string[];
23
+ /** If set, resource groups may only be created in these locations. Empty = any. */
24
+ locationAllowlist: string[];
25
+ allowDelete: boolean;
26
+ requireConfirmation: boolean;
27
+ dryRun: boolean;
28
+ auditLog: boolean;
29
+ }
30
+ export declare class PolicyError extends Error {
31
+ constructor(message: string);
32
+ }
33
+ export interface GuardContext {
34
+ tool: string;
35
+ capability: Capability;
36
+ subscription?: string;
37
+ resourceGroup?: string;
38
+ location?: string;
39
+ destructive?: boolean;
40
+ confirmExpected?: string;
41
+ confirmProvided?: string;
42
+ }
43
+ export declare class SecurityPolicy {
44
+ private readonly config;
45
+ constructor(config: SecurityConfig);
46
+ get mode(): AccessMode;
47
+ isCapabilityEnabled(capability: Capability): boolean;
48
+ isSubscriptionAllowed(sub: string): boolean;
49
+ isResourceGroupAllowed(rg: string): boolean;
50
+ isResourceGroupProtected(rg: string): boolean;
51
+ isLocationAllowed(loc: string): boolean;
52
+ guard(ctx: GuardContext): {
53
+ dryRun: boolean;
54
+ };
55
+ private audit;
56
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Governance & security policy engine for Azure Resource Manager.
3
+ *
4
+ * Shared model (access modes + capability gating + delete gating + dry-run +
5
+ * audit) plus Azure governance controls:
6
+ * - Subscription and resource-group allowlists.
7
+ * - Protected resource groups: readable, never mutated or deleted.
8
+ * - Location allowlist: new resource groups may only be created in approved regions.
9
+ * - High-impact confirmation: destructive ops must echo back the exact target name.
10
+ *
11
+ * Pure logic, no I/O — fully unit-testable.
12
+ */
13
+ const MODE_RANK = { "read-only": 0, "read-write": 1, admin: 2 };
14
+ const CAPABILITY_RANK = { read: 0, write: 1, admin: 2 };
15
+ export class PolicyError extends Error {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = "PolicyError";
19
+ }
20
+ }
21
+ export class SecurityPolicy {
22
+ config;
23
+ constructor(config) {
24
+ this.config = config;
25
+ }
26
+ get mode() {
27
+ return this.config.mode;
28
+ }
29
+ isCapabilityEnabled(capability) {
30
+ return CAPABILITY_RANK[capability] <= MODE_RANK[this.config.mode];
31
+ }
32
+ isSubscriptionAllowed(sub) {
33
+ if (this.config.subscriptionAllowlist.length === 0)
34
+ return true;
35
+ return this.config.subscriptionAllowlist.includes(sub);
36
+ }
37
+ isResourceGroupAllowed(rg) {
38
+ if (this.config.resourceGroupAllowlist.length === 0)
39
+ return true;
40
+ return this.config.resourceGroupAllowlist.includes(rg);
41
+ }
42
+ isResourceGroupProtected(rg) {
43
+ return this.config.protectedResourceGroups.includes(rg);
44
+ }
45
+ isLocationAllowed(loc) {
46
+ if (this.config.locationAllowlist.length === 0)
47
+ return true;
48
+ return this.config.locationAllowlist.includes(loc.toLowerCase().replace(/\s+/g, ""));
49
+ }
50
+ guard(ctx) {
51
+ if (!this.isCapabilityEnabled(ctx.capability)) {
52
+ this.audit(ctx, "DENY", `capability '${ctx.capability}' exceeds mode '${this.config.mode}'`);
53
+ throw new PolicyError(`Operation '${ctx.tool}' requires '${ctx.capability}' access but the server runs in '${this.config.mode}' mode.`);
54
+ }
55
+ if (ctx.subscription !== undefined && !this.isSubscriptionAllowed(ctx.subscription)) {
56
+ this.audit(ctx, "DENY", `subscription '${ctx.subscription}' not in allowlist`);
57
+ throw new PolicyError(`Subscription '${ctx.subscription}' is not in the configured allowlist (AZURE_SUBSCRIPTION_ALLOWLIST).`);
58
+ }
59
+ if (ctx.resourceGroup !== undefined) {
60
+ if (!this.isResourceGroupAllowed(ctx.resourceGroup)) {
61
+ this.audit(ctx, "DENY", `resource group '${ctx.resourceGroup}' not in allowlist`);
62
+ throw new PolicyError(`Resource group '${ctx.resourceGroup}' is not in the configured allowlist (AZURE_RESOURCE_GROUP_ALLOWLIST).`);
63
+ }
64
+ if (ctx.capability !== "read" && this.isResourceGroupProtected(ctx.resourceGroup)) {
65
+ this.audit(ctx, "DENY", `resource group '${ctx.resourceGroup}' is protected`);
66
+ throw new PolicyError(`Resource group '${ctx.resourceGroup}' is protected (AZURE_PROTECTED_RESOURCE_GROUPS); mutations are refused.`);
67
+ }
68
+ }
69
+ if (ctx.location !== undefined && !this.isLocationAllowed(ctx.location)) {
70
+ this.audit(ctx, "DENY", `location '${ctx.location}' not in allowlist`);
71
+ throw new PolicyError(`Location '${ctx.location}' is not in the configured allowlist (AZURE_LOCATION_ALLOWLIST).`);
72
+ }
73
+ if (ctx.destructive && !this.config.allowDelete) {
74
+ this.audit(ctx, "DENY", "delete not enabled");
75
+ throw new PolicyError(`Destructive operation '${ctx.tool}' is disabled. Set AZURE_ALLOW_DELETE=true to enable it.`);
76
+ }
77
+ if (ctx.destructive && this.config.requireConfirmation && ctx.confirmExpected !== undefined) {
78
+ if (ctx.confirmProvided !== ctx.confirmExpected) {
79
+ this.audit(ctx, "DENY", "confirmation mismatch");
80
+ throw new PolicyError(`Operation '${ctx.tool}' is high-impact. Re-run with confirm="${ctx.confirmExpected}" to proceed.`);
81
+ }
82
+ }
83
+ const dryRun = ctx.capability !== "read" && this.config.dryRun;
84
+ this.audit(ctx, dryRun ? "DRY_RUN" : "ALLOW");
85
+ return { dryRun };
86
+ }
87
+ audit(ctx, decision, reason) {
88
+ if (!this.config.auditLog)
89
+ return;
90
+ const line = {
91
+ ts: new Date().toISOString(),
92
+ audit: "azure-mcp",
93
+ decision,
94
+ tool: ctx.tool,
95
+ capability: ctx.capability,
96
+ subscription: ctx.subscription ?? null,
97
+ resourceGroup: ctx.resourceGroup ?? null,
98
+ destructive: ctx.destructive ?? false,
99
+ ...(reason ? { reason } : {}),
100
+ };
101
+ process.stderr.write(`${JSON.stringify(line)}\n`);
102
+ }
103
+ }
104
+ //# sourceMappingURL=security.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { AppConfig } from "./config.js";
3
+ export declare function buildServer(config: AppConfig): {
4
+ server: McpServer;
5
+ enabled: string[];
6
+ };
package/dist/server.js ADDED
@@ -0,0 +1,44 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { AzureClient, AzureError } from "./azure/client.js";
3
+ import { PolicyError, SecurityPolicy } from "./security.js";
4
+ import { adminTools } from "./tools/admin.js";
5
+ import { readTools } from "./tools/read.js";
6
+ import { writeTools } from "./tools/write.js";
7
+ const ALL_TOOLS = [...readTools, ...writeTools, ...adminTools];
8
+ export function buildServer(config) {
9
+ const policy = new SecurityPolicy(config.security);
10
+ const client = new AzureClient(config.connection);
11
+ const ctx = { client, policy, defaultSubscription: config.connection.defaultSubscription };
12
+ const server = new McpServer({ name: "mcp-azure", version: "0.1.0" });
13
+ const enabled = [];
14
+ for (const tool of ALL_TOOLS) {
15
+ if (!policy.isCapabilityEnabled(tool.capability))
16
+ continue;
17
+ enabled.push(tool.name);
18
+ server.registerTool(tool.name, tool.config, async (args) => {
19
+ try {
20
+ return await tool.handler(args ?? {}, ctx);
21
+ }
22
+ catch (err) {
23
+ return toErrorResult(err);
24
+ }
25
+ });
26
+ }
27
+ return { server, enabled };
28
+ }
29
+ function toErrorResult(err) {
30
+ let message;
31
+ if (err instanceof PolicyError)
32
+ message = `Policy denied: ${err.message}`;
33
+ else if (err instanceof AzureError)
34
+ message = `Azure error (${err.status}): ${err.message}${err.body ? `\n${truncate(err.body)}` : ""}`;
35
+ else if (err instanceof Error)
36
+ message = `${err.name}: ${err.message}`;
37
+ else
38
+ message = String(err);
39
+ return { content: [{ type: "text", text: message }], isError: true };
40
+ }
41
+ function truncate(s, max = 800) {
42
+ return s.length > max ? `${s.slice(0, max)}…` : s;
43
+ }
44
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,SAAS,GAAc,CAAC,GAAG,SAAS,EAAE,GAAG,UAAU,EAAE,GAAG,UAAU,CAAC,CAAC;AAE1E,MAAM,UAAU,WAAW,CAAC,MAAiB;IAC3C,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAClD,MAAM,GAAG,GAAgB,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC;IACxG,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAEtE,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE,SAAS;QAC3D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAA6B,EAAE,EAAE;YAClF,IAAI,CAAC;gBACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;YAC7C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC7B,CAAC;AAED,SAAS,aAAa,CAAC,GAAY;IACjC,IAAI,OAAe,CAAC;IACpB,IAAI,GAAG,YAAY,WAAW;QAAE,OAAO,GAAG,kBAAkB,GAAG,CAAC,OAAO,EAAE,CAAC;SACrE,IAAI,GAAG,YAAY,UAAU;QAAE,OAAO,GAAG,gBAAgB,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;SACnI,IAAI,GAAG,YAAY,KAAK;QAAE,OAAO,GAAG,GAAG,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;;QAClE,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAChF,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAG,GAAG,GAAG;IACpC,OAAO,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ToolDef } from "./types.js";
2
+ /**
3
+ * Admin (destructive) tools. Each needs admin mode + AZURE_ALLOW_DELETE, and —
4
+ * when AZURE_REQUIRE_CONFIRMATION is on — a `confirm` value matching the target name.
5
+ */
6
+ export declare const adminTools: ToolDef[];
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ import { jsonResult, resolveSub, textResult } from "./types.js";
3
+ const subArg = { subscription: z.string().optional().describe("Subscription id (defaults to AZURE_SUBSCRIPTION_ID)") };
4
+ /**
5
+ * Admin (destructive) tools. Each needs admin mode + AZURE_ALLOW_DELETE, and —
6
+ * when AZURE_REQUIRE_CONFIRMATION is on — a `confirm` value matching the target name.
7
+ */
8
+ export const adminTools = [
9
+ {
10
+ name: "delete_resource_group",
11
+ capability: "admin",
12
+ config: {
13
+ title: "Delete resource group",
14
+ description: "Permanently delete a resource group and everything in it. Requires admin mode, AZURE_ALLOW_DELETE=true, " +
15
+ "and confirm equal to the resource group name. Irreversible.",
16
+ inputSchema: {
17
+ ...subArg,
18
+ name: z.string().describe("Resource group name"),
19
+ confirm: z.string().optional().describe("Must equal the resource group name to proceed"),
20
+ },
21
+ },
22
+ handler: async (a, ctx) => {
23
+ const sub = resolveSub(a, ctx);
24
+ const name = a.name;
25
+ const { dryRun } = ctx.policy.guard({
26
+ tool: "delete_resource_group",
27
+ capability: "admin",
28
+ subscription: sub,
29
+ resourceGroup: name,
30
+ destructive: true,
31
+ confirmExpected: name,
32
+ confirmProvided: a.confirm,
33
+ });
34
+ if (dryRun)
35
+ return textResult(`[dry-run] Would delete resource group '${name}' and all its resources.`);
36
+ await ctx.client.deleteResourceGroup(sub, name);
37
+ return jsonResult({ deleted: true, resourceGroup: name });
38
+ },
39
+ },
40
+ {
41
+ name: "delete_resource",
42
+ capability: "admin",
43
+ config: {
44
+ title: "Delete a resource",
45
+ description: "Permanently delete a single resource by ARM id. Requires admin mode, AZURE_ALLOW_DELETE=true, and confirm " +
46
+ "equal to the resource name. Provide the api-version for the resource type. Irreversible.",
47
+ inputSchema: {
48
+ resourceId: z.string().describe("Full ARM resource id"),
49
+ resourceName: z.string().describe("Resource name — used for the confirmation check"),
50
+ apiVersion: z.string().describe("API version for this resource type"),
51
+ resourceGroup: z.string().optional().describe("Resource group (for scoping/audit)"),
52
+ confirm: z.string().optional().describe("Must equal the resource name to proceed"),
53
+ },
54
+ },
55
+ handler: async (a, ctx) => {
56
+ const name = a.resourceName;
57
+ const { dryRun } = ctx.policy.guard({
58
+ tool: "delete_resource",
59
+ capability: "admin",
60
+ resourceGroup: a.resourceGroup,
61
+ destructive: true,
62
+ confirmExpected: name,
63
+ confirmProvided: a.confirm,
64
+ });
65
+ if (dryRun)
66
+ return textResult(`[dry-run] Would delete resource '${name}' (${a.resourceId}).`);
67
+ await ctx.client.deleteResource(a.resourceId, a.apiVersion);
68
+ return jsonResult({ deleted: true, resource: name });
69
+ },
70
+ },
71
+ ];
72
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/tools/admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ToolDef } from "./types.js";
2
+ export declare const readTools: ToolDef[];
@@ -0,0 +1,76 @@
1
+ import { z } from "zod";
2
+ import { jsonResult, resolveSub } from "./types.js";
3
+ const subArg = { subscription: z.string().optional().describe("Subscription id (defaults to AZURE_SUBSCRIPTION_ID)") };
4
+ export const readTools = [
5
+ {
6
+ name: "list_subscriptions",
7
+ capability: "read",
8
+ config: { title: "List subscriptions", description: "List subscriptions the service principal can see (filtered by allowlist).", inputSchema: {} },
9
+ handler: async (_a, { client, policy }) => {
10
+ policy.guard({ tool: "list_subscriptions", capability: "read" });
11
+ const data = (await client.listSubscriptions());
12
+ const subs = (data.value ?? [])
13
+ .filter((s) => policy.isSubscriptionAllowed(s.subscriptionId))
14
+ .map((s) => ({ subscriptionId: s.subscriptionId, displayName: s.displayName, state: s.state }));
15
+ return jsonResult(subs);
16
+ },
17
+ },
18
+ {
19
+ name: "list_locations",
20
+ capability: "read",
21
+ config: { title: "List locations", description: "List the regions available to a subscription.", inputSchema: { ...subArg } },
22
+ handler: async (a, ctx) => {
23
+ const sub = resolveSub(a, ctx);
24
+ ctx.policy.guard({ tool: "list_locations", capability: "read", subscription: sub });
25
+ return jsonResult(await ctx.client.listLocations(sub));
26
+ },
27
+ },
28
+ {
29
+ name: "list_resource_groups",
30
+ capability: "read",
31
+ config: { title: "List resource groups", description: "List resource groups in a subscription (filtered by allowlist).", inputSchema: { ...subArg } },
32
+ handler: async (a, ctx) => {
33
+ const sub = resolveSub(a, ctx);
34
+ ctx.policy.guard({ tool: "list_resource_groups", capability: "read", subscription: sub });
35
+ const data = (await ctx.client.listResourceGroups(sub));
36
+ const groups = (data.value ?? [])
37
+ .filter((g) => ctx.policy.isResourceGroupAllowed(g.name))
38
+ .map((g) => ({ name: g.name, location: g.location, protected: ctx.policy.isResourceGroupProtected(g.name) }));
39
+ return jsonResult(groups);
40
+ },
41
+ },
42
+ {
43
+ name: "list_resources",
44
+ capability: "read",
45
+ config: {
46
+ title: "List resources",
47
+ description: "List resources in a subscription, or within a specific resource group.",
48
+ inputSchema: { ...subArg, resourceGroup: z.string().optional().describe("Limit to this resource group") },
49
+ },
50
+ handler: async (a, ctx) => {
51
+ const sub = resolveSub(a, ctx);
52
+ const rg = a.resourceGroup;
53
+ ctx.policy.guard({ tool: "list_resources", capability: "read", subscription: sub, resourceGroup: rg });
54
+ const data = (await ctx.client.listResources(sub, rg));
55
+ const items = (data.value ?? []).map((r) => ({ id: r.id, name: r.name, type: r.type, location: r.location, kind: r.kind }));
56
+ return jsonResult(items);
57
+ },
58
+ },
59
+ {
60
+ name: "get_resource",
61
+ capability: "read",
62
+ config: {
63
+ title: "Get resource",
64
+ description: "Fetch a resource by its full ARM id. Provide the api-version for the resource type.",
65
+ inputSchema: {
66
+ resourceId: z.string().describe("Full ARM resource id (/subscriptions/…/providers/…)"),
67
+ apiVersion: z.string().describe("API version for this resource type, e.g. 2023-07-01"),
68
+ },
69
+ },
70
+ handler: async (a, ctx) => {
71
+ ctx.policy.guard({ tool: "get_resource", capability: "read" });
72
+ return jsonResult(await ctx.client.getResource(a.resourceId, a.apiVersion));
73
+ },
74
+ },
75
+ ];
76
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { ZodRawShape } from "zod";
2
+ import type { AzureClient } from "../azure/client.js";
3
+ import type { Capability, SecurityPolicy } from "../security.js";
4
+ export interface ToolContext {
5
+ client: AzureClient;
6
+ policy: SecurityPolicy;
7
+ defaultSubscription?: string;
8
+ }
9
+ export interface ToolResult {
10
+ content: Array<{
11
+ type: "text";
12
+ text: string;
13
+ }>;
14
+ isError?: boolean;
15
+ [key: string]: unknown;
16
+ }
17
+ export interface ToolDef<Shape extends ZodRawShape = ZodRawShape> {
18
+ name: string;
19
+ capability: Capability;
20
+ config: {
21
+ title: string;
22
+ description: string;
23
+ inputSchema: Shape;
24
+ };
25
+ handler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<ToolResult>;
26
+ }
27
+ export declare function jsonResult(value: unknown): ToolResult;
28
+ export declare function textResult(text: string): ToolResult;
29
+ /** Resolve the subscription for a call: explicit arg, else the configured default. */
30
+ export declare function resolveSub(args: Record<string, unknown>, ctx: ToolContext): string;
@@ -0,0 +1,14 @@
1
+ export function jsonResult(value) {
2
+ return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
3
+ }
4
+ export function textResult(text) {
5
+ return { content: [{ type: "text", text }] };
6
+ }
7
+ /** Resolve the subscription for a call: explicit arg, else the configured default. */
8
+ export function resolveSub(args, ctx) {
9
+ const sub = args.subscription ?? ctx.defaultSubscription;
10
+ if (!sub)
11
+ throw new Error("No subscription provided and AZURE_SUBSCRIPTION_ID is not set.");
12
+ return sub;
13
+ }
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"AAuBA,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC/E,CAAC;AACD,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,UAAU,CAAC,IAA6B,EAAE,GAAgB;IACxE,MAAM,GAAG,GAAI,IAAI,CAAC,YAAmC,IAAI,GAAG,CAAC,mBAAmB,CAAC;IACjF,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IAC5F,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ToolDef } from "./types.js";
2
+ export declare const writeTools: ToolDef[];
@@ -0,0 +1,84 @@
1
+ import { z } from "zod";
2
+ import { jsonResult, resolveSub, textResult } from "./types.js";
3
+ const subArg = { subscription: z.string().optional().describe("Subscription id (defaults to AZURE_SUBSCRIPTION_ID)") };
4
+ export const writeTools = [
5
+ {
6
+ name: "create_resource_group",
7
+ capability: "write",
8
+ config: {
9
+ title: "Create resource group",
10
+ description: "Create a resource group in an approved location.",
11
+ inputSchema: {
12
+ ...subArg,
13
+ name: z.string().describe("Resource group name"),
14
+ location: z.string().describe("Azure region, e.g. eastus"),
15
+ tags: z.record(z.string()).optional().describe("Optional tags"),
16
+ },
17
+ },
18
+ handler: async (a, ctx) => {
19
+ const sub = resolveSub(a, ctx);
20
+ const name = a.name;
21
+ const location = a.location;
22
+ const { dryRun } = ctx.policy.guard({
23
+ tool: "create_resource_group",
24
+ capability: "write",
25
+ subscription: sub,
26
+ resourceGroup: name,
27
+ location,
28
+ });
29
+ if (dryRun)
30
+ return textResult(`[dry-run] Would create resource group '${name}' in ${location}.`);
31
+ return jsonResult(await ctx.client.createResourceGroup(sub, name, location, a.tags));
32
+ },
33
+ },
34
+ {
35
+ name: "tag_resource",
36
+ capability: "write",
37
+ config: {
38
+ title: "Tag a resource",
39
+ description: "Merge tags onto any resource by its ARM id (uses the generic Tags API).",
40
+ inputSchema: {
41
+ ...subArg,
42
+ resourceId: z.string().describe("Full ARM resource id"),
43
+ resourceGroup: z.string().optional().describe("Resource group (for scoping/audit)"),
44
+ tags: z.record(z.string()).describe("Tags to merge, e.g. { \"env\": \"prod\", \"owner\": \"team-a\" }"),
45
+ },
46
+ },
47
+ handler: async (a, ctx) => {
48
+ const sub = resolveSub(a, ctx);
49
+ const { dryRun } = ctx.policy.guard({
50
+ tool: "tag_resource",
51
+ capability: "write",
52
+ subscription: sub,
53
+ resourceGroup: a.resourceGroup,
54
+ });
55
+ if (dryRun)
56
+ return textResult(`[dry-run] Would merge tags onto ${a.resourceId}: ${JSON.stringify(a.tags)}`);
57
+ return jsonResult(await ctx.client.tagResource(a.resourceId, a.tags));
58
+ },
59
+ },
60
+ {
61
+ name: "control_vm",
62
+ capability: "write",
63
+ config: {
64
+ title: "Control a VM (power state)",
65
+ description: "Start, stop (deallocate), power off, or restart a virtual machine.",
66
+ inputSchema: {
67
+ ...subArg,
68
+ resourceGroup: z.string().describe("Resource group of the VM"),
69
+ vmName: z.string().describe("Virtual machine name"),
70
+ action: z.enum(["start", "powerOff", "restart", "deallocate"]).describe("Power action"),
71
+ },
72
+ },
73
+ handler: async (a, ctx) => {
74
+ const sub = resolveSub(a, ctx);
75
+ const rg = a.resourceGroup;
76
+ const { dryRun } = ctx.policy.guard({ tool: "control_vm", capability: "write", subscription: sub, resourceGroup: rg });
77
+ if (dryRun)
78
+ return textResult(`[dry-run] Would ${a.action} VM ${a.vmName} in ${rg}.`);
79
+ await ctx.client.controlVm(sub, rg, a.vmName, a.action);
80
+ return jsonResult({ ok: true, vm: a.vmName, action: a.action, resourceGroup: rg });
81
+ },
82
+ },
83
+ ];
84
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/tools/write.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@dockndevai/mcp-azure",
3
+ "mcpName": "io.github.dockndevai/mcp-azure",
4
+ "version": "0.1.0",
5
+ "description": "Model Context Protocol server for Azure (Resource Manager) — inventory, tags, VM power, and lifecycle with governance controls (access modes, subscription/resource-group scoping, protected groups, location allowlist, delete gating, confirmation, dry-run, audit).",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "repository": { "type": "git", "url": "git+https://github.com/dockndevai/mcp-azure.git" },
9
+ "bugs": { "url": "https://github.com/dockndevai/mcp-azure/issues" },
10
+ "homepage": "https://github.com/dockndevai/mcp-azure#readme",
11
+ "bin": { "mcp-azure": "dist/index.js" },
12
+ "files": ["dist"],
13
+ "engines": { "node": ">=20" },
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json",
16
+ "dev": "tsx watch src/index.ts",
17
+ "start": "node dist/index.js",
18
+ "prepublishOnly": "npm run build",
19
+ "typecheck": "tsc -p tsconfig.json --noEmit",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "lint": "tsc -p tsconfig.json --noEmit"
23
+ },
24
+ "keywords": ["mcp", "model-context-protocol", "azure", "azure-resource-manager", "cloud", "devops", "governance", "llm", "ai"],
25
+ "dependencies": {
26
+ "@modelcontextprotocol/sdk": "^1.12.0",
27
+ "zod": "^3.23.8"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.9.0",
31
+ "tsx": "^4.19.2",
32
+ "typescript": "^5.6.3",
33
+ "vitest": "^3.2.7"
34
+ }
35
+ }