claudeunmask 1.0.28 → 1.0.30

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/fromClaude.txt ADDED
@@ -0,0 +1,127 @@
1
+ // claudeunmask/index.js (or index.mjs)
2
+ const crypto = typeof window !== 'undefined'
3
+ ? window.crypto
4
+ : require('crypto').webcrypto;
5
+
6
+ function base64ToBytes(base64) {
7
+ if (typeof Buffer !== 'undefined') {
8
+ // Node.js
9
+ return new Uint8Array(Buffer.from(base64, 'base64'));
10
+ } else {
11
+ // Browser
12
+ const binary = atob(base64);
13
+ const bytes = new Uint8Array(binary.length);
14
+ for (let i = 0; i < binary.length; i++) {
15
+ bytes[i] = binary.charCodeAt(i);
16
+ }
17
+ return bytes;
18
+ }
19
+ }
20
+
21
+ async function deriveKeyFromPassword(password, salt) {
22
+ const encoder = new TextEncoder();
23
+ const pwUtf8 = encoder.encode(password);
24
+
25
+ const keyMaterial = await crypto.subtle.importKey(
26
+ 'raw',
27
+ pwUtf8,
28
+ 'PBKDF2',
29
+ false,
30
+ ['deriveKey']
31
+ );
32
+
33
+ return await crypto.subtle.deriveKey(
34
+ {
35
+ name: 'PBKDF2',
36
+ salt: salt,
37
+ iterations: 100000,
38
+ hash: 'SHA-256',
39
+ },
40
+ keyMaterial,
41
+ { name: 'AES-GCM', length: 256 },
42
+ true,
43
+ ['encrypt', 'decrypt']
44
+ );
45
+ }
46
+
47
+ async function decryptData(ciphertext, key, iv) {
48
+ const decoder = new TextDecoder();
49
+ const plaintextBuffer = await crypto.subtle.decrypt(
50
+ { name: 'AES-GCM', iv },
51
+ key,
52
+ ciphertext
53
+ );
54
+ return decoder.decode(new Uint8Array(plaintextBuffer));
55
+ }
56
+
57
+ async function unmask(theData, encryptKey, dailyKey) {
58
+ const retVal = {
59
+ data: [],
60
+ errorCount: 0,
61
+ unmaskCount: 0
62
+ };
63
+
64
+ let [tmpPassword, salt, theIV] = encryptKey.split("-");
65
+
66
+ // Try to decrypt the daily key (add padding if needed)
67
+ let decryptedPassword = null;
68
+ for (let ii = 0; ii <= 2; ii++) {
69
+ try {
70
+ const dailyKeyBinary = await deriveKeyFromPassword(
71
+ dailyKey,
72
+ base64ToBytes(salt)
73
+ );
74
+ decryptedPassword = await decryptData(
75
+ base64ToBytes(tmpPassword),
76
+ dailyKeyBinary,
77
+ base64ToBytes(theIV)
78
+ );
79
+ break;
80
+ } catch (error) {
81
+ if (ii < 2) {
82
+ dailyKey = dailyKey + '=';
83
+ } else {
84
+ throw new Error("Failed to decrypt the key");
85
+ }
86
+ }
87
+ }
88
+
89
+ const encryptionKey = await deriveKeyFromPassword(
90
+ decryptedPassword,
91
+ base64ToBytes(salt)
92
+ );
93
+
94
+ for (const curRec of theData) {
95
+ const tmpRec = {};
96
+ for (const [key, value] of Object.entries(curRec)) {
97
+ try {
98
+ if (typeof value === "string" && value.includes("m$k_")) {
99
+ retVal.unmaskCount++;
100
+ const tmpData = base64ToBytes(value.replace("m$k_", ""));
101
+ tmpRec[key] = await decryptData(
102
+ tmpData,
103
+ encryptionKey,
104
+ base64ToBytes(theIV)
105
+ );
106
+ } else {
107
+ tmpRec[key] = value;
108
+ }
109
+ } catch (error) {
110
+ tmpRec[key] = "failed";
111
+ retVal.errorCount++;
112
+ }
113
+ }
114
+ retVal.data.push(tmpRec);
115
+ }
116
+
117
+ return retVal;
118
+ }
119
+
120
+ // Export for both Node.js and browser
121
+ if (typeof module !== 'undefined' && module.exports) {
122
+ // Node.js
123
+ module.exports = { unmask, deriveKeyFromPassword, decryptData, base64ToBytes };
124
+ } else {
125
+ // Browser
126
+ window.claudeUnmask = { unmask, deriveKeyFromPassword, decryptData, base64ToBytes };
127
+ }
package/index.js CHANGED
@@ -1,19 +1,22 @@
1
- /* v 1.0.28 */
2
- function getMessageEncoding(message) {
3
- return new TextEncoder().encode(message);
4
- }
5
-
6
- function getMessageDecoding(buffer) {
7
- return new TextDecoder().decode(buffer);
8
- }
1
+ /* v 1.0.30 */
2
+ // claudeunmask/index.js (or index.mjs)
3
+ const crypto = typeof window !== 'undefined'
4
+ ? window.crypto
5
+ : require('crypto').webcrypto;
9
6
 
10
7
  function base64ToBytes(base64) {
11
- const binary = atob(base64);
12
- const bytes = new Uint8Array(binary.length);
13
- for (let i = 0; i < binary.length; i++) {
14
- bytes[i] = binary.charCodeAt(i);
8
+ if (typeof Buffer !== 'undefined') {
9
+ // Node.js
10
+ return new Uint8Array(Buffer.from(base64, 'base64'));
11
+ } else {
12
+ // Browser
13
+ const binary = atob(base64);
14
+ const bytes = new Uint8Array(binary.length);
15
+ for (let i = 0; i < binary.length; i++) {
16
+ bytes[i] = binary.charCodeAt(i);
17
+ }
18
+ return bytes;
15
19
  }
16
- return bytes;
17
20
  }
18
21
 
19
22
  function bytesToBase64(bytes) {
@@ -22,7 +25,8 @@
22
25
  }
23
26
 
24
27
  async function deriveKeyFromPassword(password, salt) {
25
- const pwUtf8 = getMessageEncoding(password);
28
+ const encoder = new TextEncoder();
29
+ const pwUtf8 = encoder.encode(password);
26
30
 
27
31
  const keyMaterial = await crypto.subtle.importKey(
28
32
  'raw',
@@ -32,7 +36,7 @@
32
36
  ['deriveKey']
33
37
  );
34
38
 
35
- const derivedKey = await crypto.subtle.deriveKey(
39
+ return await crypto.subtle.deriveKey(
36
40
  {
37
41
  name: 'PBKDF2',
38
42
  salt: salt,
@@ -44,113 +48,110 @@
44
48
  true,
45
49
  ['encrypt', 'decrypt']
46
50
  );
47
- return derivedKey;
48
51
  }
49
52
 
50
53
  async function decryptThisData(ciphertext, key, iv) {
54
+ const decoder = new TextDecoder();
51
55
  try {
52
56
  const plaintextBuffer = await crypto.subtle.decrypt(
53
57
  { name: 'AES-GCM', iv },
54
58
  key,
55
59
  ciphertext
56
60
  );
57
- return getMessageDecoding(new Uint8Array(plaintextBuffer));
61
+ return decoder.decode(new Uint8Array(plaintextBuffer));
58
62
  } catch (error) {
59
- console.log ("failed", key, bytesToBase64(ciphertext), error);
60
63
  throw Error ("Failed to decrypt");
61
64
  }
62
65
  return "";
63
66
  }
64
- async function decrypt(inData, decryptionKey) {
65
- var dailyKey = await window.prompt("Enter the daily key","crazy_string");
66
- console.log ("decrypt: daily key", dailyKey);
67
- var retVal = [];
67
+
68
+ async function unmaskData (inData, decryptionKey, dailyKey) {
69
+ const retVal = {
70
+ data: [],
71
+ errorCount: 0,
72
+ unmaskCount: 0
73
+ };
68
74
  var enc = new TextEncoder();
69
75
  var [tmpPassword, salt, theIV] = decryptionKey.split("-");
70
- console.log ("from MCP", decryptionKey, tmpPassword, salt, theIV);
71
- const dailyKeyBinary = await deriveKeyFromPassword(dailyKey, base64ToBytes(salt));
72
- try {
73
- tmpPassword = await decryptThisData (base64ToBytes(tmpPassword), dailyKeyBinary, base64ToBytes(theIV));
74
- } catch (error) {
75
- console.log ("failed to decrypt the key");
76
+ var haveError = 0;
77
+ for (var ii= 0; ii<=2; ii++) { // try to decrypt the daily key and the add = until it works or we've added 2
78
+ try {
79
+ const dailyKeyBinary = await deriveKeyFromPassword(dailyKey, base64ToBytes(salt));
80
+ tmpPassword = await decryptThisData (base64ToBytes(tmpPassword), dailyKeyBinary, base64ToBytes(theIV));
81
+ // if we get here then we have a good daily key
82
+ haveError = 0;
83
+ break;
84
+ } catch (error) {
85
+ haveError = 1;
86
+ dailyKey = dailyKey + '=';
87
+ }
88
+ }
89
+ if (haveError) {
76
90
  throw Error ("Failed to decrypt the key");
77
91
  }
78
- console.log ("secret:", tmpPassword);
79
92
  const password = tmpPassword;
93
+ const encryptionKey = await deriveKeyFromPassword(password, base64ToBytes(salt));
80
94
  for (const curRec of inData) {
81
95
  var tmpRec = {};
82
96
  for (const [key, value] of Object.entries(curRec)) {
83
97
  try {
84
98
  if (typeof value === "string" && value.includes("m$k_")) {
85
- console.log ("masked", value, curRec[key]);
86
- var theData = base64ToBytes(curRec[key].replace ("m$k_", ""));
87
- const encryptionKey = await deriveKeyFromPassword(password, base64ToBytes(salt));
99
+ retVal.unmaskCount++;
100
+ const theData = base64ToBytes(value.replace ("m$k_", ""));
88
101
  tmpRec[key] = await decryptThisData (theData, encryptionKey, base64ToBytes(theIV));
89
102
  } else {
90
103
  tmpRec[key] = value;
91
104
  }
92
105
  } catch (error) {
93
- console.log ("error", error);
94
106
  tmpRec[key] = "failed";
107
+ retVal.errorCount++;
95
108
  }
96
109
  }
97
- retVal.push(tmpRec);
110
+ retVal.data.push(tmpRec);
111
+ }
112
+
113
+ if (retVal.errorCount) {
114
+ console.log ("an error happened", (100 * retVal.errorCount / retVal.unmaskCount));
98
115
  }
99
116
  return retVal;
100
117
  }
101
- async function unmaskData (inData, decryptionKey, dailyKey) {
102
- console.log ("unmask: daily key", dailyKey);
103
- var retVal = {};
104
- retVal.data = [];
105
- retVal.errorCount = 0;
106
- retVal.unmaskCount = 0;
118
+
119
+ async function decrypt(inData, decryptionKey) {
120
+ var dailyKey = await window.prompt("Enter the daily key","crazy_string");
121
+ var retVal = [];
107
122
  var enc = new TextEncoder();
108
123
  var [tmpPassword, salt, theIV] = decryptionKey.split("-");
109
- console.log ("from MCP", decryptionKey, tmpPassword, salt, theIV);
110
- var haveError = 0;
111
- for (var ii= 0; ii<=2; ii++) { // try to decrypt the daily key and the add = until it works or we've added 2
112
- console.log ("testing daily key: ", dailyKey);
113
- const dailyKeyBinary = await deriveKeyFromPassword(dailyKey, base64ToBytes(salt));
114
- try {
115
- tmpPassword = await decryptThisData (base64ToBytes(tmpPassword), dailyKeyBinary, base64ToBytes(theIV));
116
- // if we get here then we have a good daily key
117
- haveError = 0;
118
- break;
119
- } catch (error) {
120
- haveError = 1;
121
- dailyKey = dailyKey + '=';
122
- }
123
- }
124
- if (haveError) {
124
+ const dailyKeyBinary = await deriveKeyFromPassword(dailyKey, base64ToBytes(salt));
125
+ try {
126
+ tmpPassword = await decryptThisData (base64ToBytes(tmpPassword), dailyKeyBinary, base64ToBytes(theIV));
127
+ } catch (error) {
125
128
  throw Error ("Failed to decrypt the key");
126
129
  }
127
- console.log ("secret:", tmpPassword);
128
130
  const password = tmpPassword;
129
- const encryptionKey = await deriveKeyFromPassword(password, base64ToBytes(salt));
130
131
  for (const curRec of inData) {
131
132
  var tmpRec = {};
132
133
  for (const [key, value] of Object.entries(curRec)) {
133
134
  try {
134
135
  if (typeof value === "string" && value.includes("m$k_")) {
135
- console.log ("masked", value, curRec[key]);
136
- retVal.unmaskCount++;
137
136
  var theData = base64ToBytes(curRec[key].replace ("m$k_", ""));
137
+ const encryptionKey = await deriveKeyFromPassword(password, base64ToBytes(salt));
138
138
  tmpRec[key] = await decryptThisData (theData, encryptionKey, base64ToBytes(theIV));
139
139
  } else {
140
140
  tmpRec[key] = value;
141
141
  }
142
142
  } catch (error) {
143
- console.log ("error", error);
144
143
  tmpRec[key] = "failed";
145
- retVal.errorCount++;
146
144
  }
147
145
  }
148
- retVal.data.push(tmpRec);
149
- }
150
-
151
- if (retVal.errorCount) {
152
- console.log ("an error happened", (100 * retVal.errorCount / retVal.unmaskCount));
153
-
146
+ retVal.push(tmpRec);
154
147
  }
155
148
  return retVal;
156
- }
149
+ }
150
+ // Export for both Node.js and browser
151
+ if (typeof module !== 'undefined' && module.exports) {
152
+ // Node.js
153
+ module.exports = { unmaskData, deriveKeyFromPassword, decryptThisData, base64ToBytes };
154
+ } else {
155
+ // Browser
156
+ window.claudeUnmask = { unmaskData, deriveKeyFromPassword, decryptThisData, base64ToBytes };
157
+ }
package/index.min.js CHANGED
@@ -1 +1 @@
1
- function getMessageEncoding(e){return new TextEncoder().encode(e)}function getMessageDecoding(e){return new TextDecoder().decode(e)}function base64ToBytes(e){let t=atob(e),a=new Uint8Array(t.length);for(let r=0;r<t.length;r++)a[r]=t.charCodeAt(r);return a}function bytesToBase64(e){let t=String.fromCharCode(...e);return btoa(t)}async function deriveKeyFromPassword(e,t){let a=getMessageEncoding(e),r=await crypto.subtle.importKey("raw",a,"PBKDF2",!1,["deriveKey"]),o=await crypto.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},r,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"]);return o}async function decryptThisData(e,t,a){try{let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:a},t,e);return getMessageDecoding(new Uint8Array(r))}catch(o){throw console.log("failed",t,bytesToBase64(e),o),Error("Failed to decrypt")}return""}async function decrypt(e,t){var a=await window.prompt("Enter the daily key","crazy_string");console.log("decrypt: daily key",a);var r=[];new TextEncoder;var[o,s,n]=t.split("-");console.log("from MCP",t,o,s,n);let i=await deriveKeyFromPassword(a,base64ToBytes(s));try{o=await decryptThisData(base64ToBytes(o),i,base64ToBytes(n))}catch(y){throw console.log("failed to decrypt the key"),Error("Failed to decrypt the key")}console.log("secret:",o);let l=o;for(let d of e){var c={};for(let[u,g]of Object.entries(d))try{if("string"==typeof g&&g.includes("m$k_")){console.log("masked",g,d[u]);var f=base64ToBytes(d[u].replace("m$k_",""));let h=await deriveKeyFromPassword(l,base64ToBytes(s));c[u]=await decryptThisData(f,h,base64ToBytes(n))}else c[u]=g}catch(p){console.log("error",p),c[u]="failed"}r.push(c)}return r}async function unmaskData(e,t,a){console.log("unmask: daily key",a);var r={};r.data=[],r.errorCount=0,r.unmaskCount=0,new TextEncoder;var[o,s,n]=t.split("-");console.log("from MCP",t,o,s,n);let i=await deriveKeyFromPassword(a,base64ToBytes(s));try{o=await decryptThisData(base64ToBytes(o),i,base64ToBytes(n))}catch(y){throw console.log("failed to decrypt the key"),Error("Failed to decrypt the key")}console.log("secret:",o);let l=o,d=await deriveKeyFromPassword(l,base64ToBytes(s));for(let c of e){var u={};for(let[g,f]of Object.entries(c))try{if("string"==typeof f&&f.includes("m$k_")){console.log("masked",f,c[g]),r.unmaskCount++;var h=base64ToBytes(c[g].replace("m$k_",""));u[g]=await decryptThisData(h,d,base64ToBytes(n))}else u[g]=f}catch(p){console.log("error",p),u[g]="failed",r.errorCount++}r.data.push(u)}return r.errorCount&&console.log("an error happened",100*r.errorCount/r.unmaskCount),r}
1
+ const crypto="undefined"!=typeof window?window.crypto:require("crypto").webcrypto;function base64ToBytes(e){if("undefined"!=typeof Buffer)return new Uint8Array(Buffer.from(e,"base64"));{let t=atob(e),a=new Uint8Array(t.length);for(let r=0;r<t.length;r++)a[r]=t.charCodeAt(r);return a}}function bytesToBase64(e){let t=String.fromCharCode(...e);return btoa(t)}async function deriveKeyFromPassword(e,t){let a=new TextEncoder,r=a.encode(e),o=await crypto.subtle.importKey("raw",r,"PBKDF2",!1,["deriveKey"]);return await crypto.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},o,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}async function decryptThisData(e,t,a){let r=new TextDecoder;try{let o=await crypto.subtle.decrypt({name:"AES-GCM",iv:a},t,e);return r.decode(new Uint8Array(o))}catch(s){throw Error("Failed to decrypt")}return""}async function unmaskData(e,t,a){let r={data:[],errorCount:0,unmaskCount:0};new TextEncoder;for(var[o,s,n]=t.split("-"),y=0,i=0;i<=2;i++)try{let d=await deriveKeyFromPassword(a,base64ToBytes(s));o=await decryptThisData(base64ToBytes(o),d,base64ToBytes(n)),y=0;break}catch(c){y=1,a+="="}if(y)throw Error("Failed to decrypt the key");let l=o,p=await deriveKeyFromPassword(l,base64ToBytes(s));for(let u of e){var f={};for(let[w,h]of Object.entries(u))try{if("string"==typeof h&&h.includes("m$k_")){r.unmaskCount++;let m=base64ToBytes(h.replace("m$k_",""));f[w]=await decryptThisData(m,p,base64ToBytes(n))}else f[w]=h}catch(T){f[w]="failed",r.errorCount++}r.data.push(f)}return r.errorCount&&console.log("an error happened",100*r.errorCount/r.unmaskCount),r}async function decrypt(e,t){var a=await window.prompt("Enter the daily key","crazy_string"),r=[];new TextEncoder;var[o,s,n]=t.split("-");let y=await deriveKeyFromPassword(a,base64ToBytes(s));try{o=await decryptThisData(base64ToBytes(o),y,base64ToBytes(n))}catch(i){throw Error("Failed to decrypt the key")}let d=o;for(let c of e){var l={};for(let[p,u]of Object.entries(c))try{if("string"==typeof u&&u.includes("m$k_")){var f=base64ToBytes(c[p].replace("m$k_",""));let w=await deriveKeyFromPassword(d,base64ToBytes(s));l[p]=await decryptThisData(f,w,base64ToBytes(n))}else l[p]=u}catch(h){l[p]="failed"}r.push(l)}return r}"undefined"!=typeof module&&module.exports?module.exports={unmaskData,deriveKeyFromPassword,decryptThisData,base64ToBytes}:window.claudeUnmask={unmaskData,deriveKeyFromPassword,decryptThisData,base64ToBytes};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudeunmask",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "claude unmask test",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/index - Copy.js DELETED
@@ -1,19 +0,0 @@
1
-
2
- function unmask (theData) {
3
- var retVal = [];
4
- for (curRow of theData) {
5
- var retRow = {};
6
- for (const [key, value] of Object.entries(curRow)) {
7
- let newValue = value;
8
-
9
- if (newValue.startsWith ("m$k_")) {
10
- newValue = newValue.replace ("m$k_","");
11
- newValue = newValue.split('').reverse().join('');
12
- }
13
- retRow[key] = newValue;
14
- }
15
- retVal.push (retRow);
16
- }
17
- console.log ("hello from unmask", retVal);
18
- return retVal;
19
- }
package/junk.js DELETED
@@ -1,676 +0,0 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import {
4
- CallToolRequestSchema,
5
- ListToolsRequestSchema,
6
- } from "@modelcontextprotocol/sdk/types.js";
7
- import { DefaultAzureCredential } from "@azure/identity";
8
- import { SqlManagementClient } from "@azure/arm-sql";
9
- import sql from "mssql";
10
- import * as fs from "fs";
11
- import * as path from "path";
12
-
13
- import { webcrypto } from 'crypto';
14
- const subtle = webcrypto.subtle;
15
-
16
- // Azure MCP Server
17
- class AzureMCPServer {
18
- private server: Server;
19
- private credential: DefaultAzureCredential;
20
- private subscriptionId: string;
21
- private sqlPools: Map<string, sql.ConnectionPool>;
22
-
23
- constructor() {
24
- this.server = new Server(
25
- {
26
- name: "azure-mcp-server",
27
- version: "1.0.0",
28
- },
29
- {
30
- capabilities: {
31
- tools: {},
32
- },
33
- }
34
- );
35
-
36
- // Get subscription ID from environment
37
- this.subscriptionId = process.env.AZURE_SUBSCRIPTION_ID || "";
38
- if (!this.subscriptionId) {
39
- throw new Error("AZURE_SUBSCRIPTION_ID environment variable is required");
40
- }
41
-
42
- // Initialize Azure credential
43
- this.credential = new DefaultAzureCredential();
44
-
45
- // Initialize SQL connection pool cache
46
- this.sqlPools = new Map();
47
-
48
- this.setupHandlers();
49
- }
50
-
51
- private setupHandlers() {
52
- // List available tools
53
- this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
54
- tools: [
55
- {
56
- name: "list_resource_groups",
57
- description: "List all resource groups in the Azure subscription",
58
- inputSchema: {
59
- type: "object",
60
- properties: {},
61
- },
62
- },
63
- {
64
- name: "create_resource_group",
65
- description: "Create a new resource group",
66
- inputSchema: {
67
- type: "object",
68
- properties: {
69
- name: { type: "string", description: "Resource group name" },
70
- location: { type: "string", description: "Azure region (e.g., eastus, westus2)" },
71
- },
72
- required: ["name", "location"],
73
- },
74
- },
75
- {
76
- name: "list_storage_accounts",
77
- description: "List all storage accounts in a resource group",
78
- inputSchema: {
79
- type: "object",
80
- properties: {
81
- resourceGroup: { type: "string", description: "Resource group name" },
82
- },
83
- required: ["resourceGroup"],
84
- },
85
- },
86
- {
87
- name: "create_storage_account",
88
- description: "Create a new storage account",
89
- inputSchema: {
90
- type: "object",
91
- properties: {
92
- resourceGroup: { type: "string", description: "Resource group name" },
93
- accountName: { type: "string", description: "Storage account name (lowercase, alphanumeric)" },
94
- location: { type: "string", description: "Azure region" },
95
- sku: { type: "string", description: "SKU name (e.g., Standard_LRS, Premium_LRS)", default: "Standard_LRS" },
96
- },
97
- required: ["resourceGroup", "accountName", "location"],
98
- },
99
- },
100
- {
101
- name: "list_vms",
102
- description: "List all virtual machines in a resource group",
103
- inputSchema: {
104
- type: "object",
105
- properties: {
106
- resourceGroup: { type: "string", description: "Resource group name" },
107
- },
108
- required: ["resourceGroup"],
109
- },
110
- },
111
- {
112
- name: "get_vm_status",
113
- description: "Get the power state of a virtual machine",
114
- inputSchema: {
115
- type: "object",
116
- properties: {
117
- resourceGroup: { type: "string", description: "Resource group name" },
118
- vmName: { type: "string", description: "Virtual machine name" },
119
- },
120
- required: ["resourceGroup", "vmName"],
121
- },
122
- },
123
- {
124
- name: "list_blob_containers",
125
- description: "List all blob containers in a storage account",
126
- inputSchema: {
127
- type: "object",
128
- properties: {
129
- accountName: { type: "string", description: "Storage account name" },
130
- },
131
- required: ["accountName"],
132
- },
133
- },
134
- {
135
- name: "upload_blob",
136
- description: "Upload content to a blob in Azure Storage",
137
- inputSchema: {
138
- type: "object",
139
- properties: {
140
- accountName: { type: "string", description: "Storage account name" },
141
- containerName: { type: "string", description: "Container name" },
142
- blobName: { type: "string", description: "Blob name/path" },
143
- content: { type: "string", description: "Content to upload" },
144
- },
145
- required: ["accountName", "containerName", "blobName", "content"],
146
- },
147
- },
148
- {
149
- name: "list_sql_servers",
150
- description: "List all SQL servers in a resource group",
151
- inputSchema: {
152
- type: "object",
153
- properties: {
154
- resourceGroup: { type: "string", description: "Resource group name" },
155
- },
156
- required: ["resourceGroup"],
157
- },
158
- },
159
- {
160
- name: "list_databases",
161
- description: "List all databases on a SQL server",
162
- inputSchema: {
163
- type: "object",
164
- properties: {
165
- resourceGroup: { type: "string", description: "Resource group name" },
166
- serverName: { type: "string", description: "SQL server name" },
167
- },
168
- required: ["resourceGroup", "serverName"],
169
- },
170
- },
171
- {
172
- name: "execute_query",
173
- description: "Execute a SQL query on an Azure SQL Database",
174
- inputSchema: {
175
- type: "object",
176
- properties: {
177
- server: { type: "string", description: "SQL server FQDN (e.g., myserver.database.windows.net)" },
178
- database: { type: "string", description: "Database name" },
179
- query: { type: "string", description: "SQL query to execute" },
180
- username: { type: "string", description: "Username for audit logging (required)" },
181
- password: { type: "string", description: "SQL password (optional, uses Azure AD if not provided)" },
182
- },
183
- required: ["server", "database", "query", "username"],
184
- },
185
- },
186
- {
187
- name: "execute_stored_procedure",
188
- description: "Execute a stored procedure on an Azure SQL Database",
189
- inputSchema: {
190
- type: "object",
191
- properties: {
192
- server: { type: "string", description: "SQL server FQDN" },
193
- database: { type: "string", description: "Database name" },
194
- procedureName: { type: "string", description: "Stored procedure name" },
195
- username: { type: "string", description: "Username for audit logging (required)" },
196
- parameters: { type: "object", description: "Procedure parameters as key-value pairs" },
197
- password: { type: "string", description: "SQL password (optional)" },
198
- },
199
- required: ["server", "database", "procedureName", "username"],
200
- },
201
- },
202
- {
203
- name: "get_database_info",
204
- description: "Get detailed information about a specific database",
205
- inputSchema: {
206
- type: "object",
207
- properties: {
208
- resourceGroup: { type: "string", description: "Resource group name" },
209
- serverName: { type: "string", description: "SQL server name" },
210
- databaseName: { type: "string", description: "Database name" },
211
- },
212
- required: ["resourceGroup", "serverName", "databaseName"],
213
- },
214
- },
215
- {
216
- name: "get_version",
217
- description: "Get the version of the Azure MCP Server",
218
- inputSchema: {
219
- type: "object",
220
- properties: {},
221
- },
222
- },
223
- ],
224
- }));
225
-
226
- // Handle tool calls
227
- this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
228
- try {
229
- switch (request.params.name) {
230
- case "list_resource_groups":
231
- return await this.listResourceGroups();
232
-
233
- case "create_resource_group":
234
- return await this.createResourceGroup(
235
- request.params.arguments as { name: string; location: string }
236
- );
237
-
238
- case "list_storage_accounts":
239
- return await this.listStorageAccounts(
240
- request.params.arguments as { resourceGroup: string }
241
- );
242
-
243
- case "create_storage_account":
244
- return await this.createStorageAccount(
245
- request.params.arguments as { resourceGroup: string; accountName: string; location: string; sku?: string }
246
- );
247
-
248
- case "list_vms":
249
- return await this.listVMs(
250
- request.params.arguments as { resourceGroup: string }
251
- );
252
-
253
- case "get_vm_status":
254
- return await this.getVMStatus(
255
- request.params.arguments as { resourceGroup: string; vmName: string }
256
- );
257
-
258
- case "list_blob_containers":
259
- return await this.listBlobContainers(
260
- request.params.arguments as { accountName: string }
261
- );
262
-
263
- case "upload_blob":
264
- return await this.uploadBlob(
265
- request.params.arguments as { accountName: string; containerName: string; blobName: string; content: string }
266
- );
267
-
268
- case "list_sql_servers":
269
- return await this.listSqlServers(
270
- request.params.arguments as { resourceGroup: string }
271
- );
272
-
273
- case "list_databases":
274
- return await this.listDatabases(
275
- request.params.arguments as { resourceGroup: string; serverName: string }
276
- );
277
-
278
- case "execute_query":
279
- return await this.executeQuery(
280
- request.params.arguments as { server: string; database: string; query: string; username: string; password?: string }
281
- );
282
-
283
- case "execute_stored_procedure":
284
- return await this.executeStoredProcedure(
285
- request.params.arguments as { server: string; database: string; procedureName: string; username: string; parameters?: any; password?: string }
286
- );
287
-
288
- case "get_database_info":
289
- return await this.getDatabaseInfo(
290
- request.params.arguments as { resourceGroup: string; serverName: string; databaseName: string }
291
- );
292
-
293
- case "get_version":
294
- return await this.getVersion();
295
-
296
- default:
297
- throw new Error(`Unknown tool: ${request.params.name}`);
298
- }
299
- } catch (error: any) {
300
- return {
301
- content: [
302
- {
303
- type: "text",
304
- text: `Error: ${error.message}`,
305
- },
306
- ],
307
- };
308
- }
309
- });
310
- }
311
-
312
- private async listSqlServers(args: { resourceGroup: string }) {
313
- const client = new SqlManagementClient(this.credential, this.subscriptionId);
314
- const servers = [];
315
-
316
- for await (const server of client.servers.listByResourceGroup(args.resourceGroup)) {
317
- servers.push({
318
- name: server.name,
319
- location: server.location,
320
- fullyQualifiedDomainName: server.fullyQualifiedDomainName,
321
- version: server.version,
322
- administratorLogin: server.administratorLogin,
323
- });
324
- }
325
-
326
- return {
327
- content: [
328
- {
329
- type: "text",
330
- text: JSON.stringify(servers, null, 2),
331
- },
332
- ],
333
- };
334
- }
335
-
336
- private async listDatabases(args: { resourceGroup: string; serverName: string }) {
337
- const client = new SqlManagementClient(this.credential, this.subscriptionId);
338
- const databases = [];
339
-
340
- for await (const db of client.databases.listByServer(args.resourceGroup, args.serverName)) {
341
- databases.push({
342
- name: db.name,
343
- location: db.location,
344
- status: db.status,
345
- sku: db.sku,
346
- maxSizeBytes: db.maxSizeBytes,
347
- creationDate: db.creationDate,
348
- });
349
- }
350
-
351
- return {
352
- content: [
353
- {
354
- type: "text",
355
- text: JSON.stringify(databases, null, 2),
356
- },
357
- ],
358
- };
359
- }
360
-
361
- private async getDatabaseInfo(args: { resourceGroup: string; serverName: string; databaseName: string }) {
362
- const client = new SqlManagementClient(this.credential, this.subscriptionId);
363
-
364
- const database = await client.databases.get(
365
- args.resourceGroup,
366
- args.serverName,
367
- args.databaseName
368
- );
369
-
370
- return {
371
- content: [
372
- {
373
- type: "text",
374
- text: JSON.stringify({
375
- name: database.name,
376
- location: database.location,
377
- status: database.status,
378
- sku: database.sku,
379
- maxSizeBytes: database.maxSizeBytes,
380
- collation: database.collation,
381
- creationDate: database.creationDate,
382
- earliestRestoreDate: database.earliestRestoreDate,
383
- elasticPoolId: database.elasticPoolId,
384
- kind: database.kind,
385
- }, null, 2),
386
- },
387
- ],
388
- };
389
- }
390
-
391
- private async getSqlPool(server: string, database: string, username: string, password?: string): Promise<sql.ConnectionPool> {
392
- const poolKey = `${server}:${database}`;
393
-
394
- // Return existing pool if available
395
- if (this.sqlPools.has(poolKey)) {
396
- const pool = this.sqlPools.get(poolKey)!;
397
- if (pool.connected) {
398
- return pool;
399
- }
400
- }
401
-
402
- const DBusername = "pprappdevusermcp";
403
- password = "ZuX?c5%+=6!c?}5z)n73"
404
-
405
-
406
- // Create new connection pool
407
- let config: sql.config;
408
-
409
- // Use SQL auth if credentials provided, otherwise use Azure AD
410
- if (DBusername && password) {
411
- config = {
412
- server: server,
413
- database: database,
414
- user: DBusername,
415
- password: password,
416
- options: {
417
- encrypt: true,
418
- trustServerCertificate: false,
419
- },
420
- pool: {
421
- max: 10,
422
- min: 0,
423
- idleTimeoutMillis: 30000,
424
- },
425
- };
426
- } else {
427
- // Use Azure AD authentication with managed identity or DefaultAzureCredential
428
- const token = await this.credential.getToken("https://database.windows.net/.default");
429
- config = {
430
- server: server,
431
- database: database,
432
- options: {
433
- encrypt: true,
434
- trustServerCertificate: false,
435
- },
436
- pool: {
437
- max: 10,
438
- min: 0,
439
- idleTimeoutMillis: 30000,
440
- },
441
- authentication: {
442
- type: 'azure-active-directory-access-token',
443
- options: {
444
- token: token.token,
445
- },
446
- } as any,
447
- };
448
- }
449
-
450
- const pool = await sql.connect(config);
451
- this.sqlPools.set(poolKey, pool);
452
-
453
- return pool;
454
- }
455
-
456
- private async logRequest(queryString: string, server: string, username: string) {
457
- const homeDir = process.env.USERPROFILE || process.env.HOME || '';
458
- const logDir = path.join(homeDir, 'source', 'repos', 'mcp_server');
459
-
460
- // Create directory if it doesn't exist
461
- if (!fs.existsSync(logDir)) {
462
- fs.mkdirSync(logDir, { recursive: true });
463
- }
464
-
465
- const logFile = path.join(logDir, 'sql_query_log.txt');
466
- const logEntry = `[${new Date().toISOString()}] Server: ${server}\tUsername: ${username}\tQuery:\n ${queryString}\n`;
467
- fs.appendFileSync(logFile, logEntry);
468
-
469
- }
470
-
471
- private getMessageEncoding(message: string): Uint8Array {
472
- return new TextEncoder().encode(message);
473
- }
474
-
475
- private getMessageDecoding(message: Uint8Array): string {
476
- return new TextDecoder().decode(message);
477
- }
478
-
479
- private bytesToBase64(bytes: Uint8Array): string {
480
- const binary = String.fromCharCode(...bytes);
481
- return btoa(binary);
482
- }
483
- private base64ToBytes(base64: string): Uint8Array {
484
- const binary = atob(base64);
485
- const bytes = new Uint8Array(binary.length);
486
- for (let i = 0; i < binary.length; i++) {
487
- bytes[i] = binary.charCodeAt(i);
488
- }
489
- return bytes;
490
- }
491
-
492
- private async deriveKeyFromPassword(password: string, salt: Uint8Array): Promise<CryptoKey> {
493
- const pwUtf8 = this.getMessageEncoding(password);
494
- // Use PBKDF2 for key derivation from a password
495
-
496
- // importKey(format, keyData, algorithm, extractable, keyUsages)
497
- const keyMaterial = await crypto.subtle.importKey(
498
- 'raw',
499
- pwUtf8 as BufferSource,
500
- 'PBKDF2',
501
- false,
502
- ['deriveKey']
503
- );
504
-
505
- const derivedKey = await crypto.subtle.deriveKey(
506
- {
507
- name: 'PBKDF2',
508
- salt: salt as BufferSource,
509
- iterations: 100000, // High iterations make it more secure
510
- hash: 'SHA-256',
511
- },
512
- keyMaterial,
513
- { name: 'AES-GCM', length: 256 }, // Use AES-GCM for encryption
514
- true, // Key can be extracted if needed
515
- ['encrypt', 'decrypt']
516
- );
517
-
518
- return derivedKey;
519
- }
520
-
521
- private async encryptData(plaintext: string, key: CryptoKey, iv: BufferSource): Promise<{ ciphertext: string, iv: string }> {
522
- const encoded = this.getMessageEncoding(plaintext);
523
-
524
- const ciphertextBuffer = await crypto.subtle.encrypt(
525
- { name: 'AES-GCM', iv},
526
- key,
527
- encoded as BufferSource
528
- );
529
- const cipherBase64 = this.bytesToBase64(new Uint8Array(ciphertextBuffer));
530
- const ivBase64 = this.bytesToBase64(new Uint8Array(iv as ArrayBuffer));
531
-
532
- return {
533
- ciphertext: cipherBase64,
534
- iv: ivBase64
535
- };
536
- }
537
-
538
- private async findDataToEncrypt(theData: any[], secretKey: string, salt:Uint8Array, ivIn: BufferSource): Promise<any[]> {
539
- const retVal: any[] = [];
540
- const algorithm: string = 'SHA-256';
541
-
542
- const server: string = "no server v1";
543
- const username: string = "no username";
544
-
545
- const encryptionKey = await this.deriveKeyFromPassword(secretKey, salt);
546
-
547
- for (const curRow of theData) {
548
- const retRow: any = {};
549
-
550
- for (const [key, value] of Object.entries(curRow)) {
551
- let newValue: string = <any>value;
552
-
553
- if (typeof newValue === 'string' && newValue.startsWith("m$k_")) {
554
- this.logRequest(`\t in encrypt -> encrypting ${JSON.stringify(newValue)}`, server, username);
555
- var tmpValue: any = newValue.replace("m$k_", "");
556
-
557
- const { ciphertext, iv } = await this.encryptData(tmpValue, encryptionKey, ivIn);
558
- // newValue = "m$k_" + ciphertext + "-" + iv + "-" + this.bytesToBase64(salt) + "-" + this.bytesToBase64(ivIn as Uint8Array);
559
- newValue = "m$k_" + ciphertext;
560
- this.logRequest(`\tin encrypt -> result ${JSON.stringify(newValue)}`, server, username);
561
- }
562
-
563
- retRow[key] = newValue;
564
- }
565
-
566
- retVal.push(retRow);
567
- }
568
-
569
- return retVal;
570
- }
571
-
572
- private async executeQuery(args: { server: string; database: string; query: string; username: string; password?: string }) {
573
- try {
574
- const pool = await this.getSqlPool(args.server, args.database, args.username, args.password);
575
-
576
- this.logRequest(args.query, args.server, args.username);
577
-
578
- const result = await pool.request().query(args.query);
579
- const resultCount = result.recordset ? result.recordset.length : 0;
580
- this.logRequest(`\t-> Query returned ${JSON.stringify(result.recordset)} rows`, args.server, args.username);
581
- const secretKey = this.bytesToBase64(crypto.getRandomValues(new Uint8Array(16)));
582
- const salt = crypto.getRandomValues(new Uint8Array(16));
583
- const ivIn = crypto.getRandomValues(new Uint8Array(16));
584
-
585
- var retval: any = await this.findDataToEncrypt(result.recordset, secretKey, salt, ivIn);
586
- const junkBits = new Date().getSeconds().toString(16).padStart(2, 'P');
587
- const secretKey_modified = junkBits + secretKey;
588
- this.logRequest(`\t-> encryption returned ${JSON.stringify(retval)}`, args.server, args.username);
589
- return {
590
- content: [
591
- {
592
- type: "text",
593
- text: JSON.stringify({
594
- rowsAffected: result.rowsAffected,
595
- key: secretKey_modified + "-" + this.bytesToBase64(salt) + "-" + this.bytesToBase64(ivIn as Uint8Array),
596
- recordset: retval,
597
- }, null, 2),
598
- },
599
- ],
600
- };
601
- } catch (error: any) {
602
- this.logRequest(`\t**** error occurred: ${error.message}`, args.server, args.username);
603
- return {
604
- content: [
605
- {
606
- type: "text",
607
- text: `Query execution failed: ${error.message}`,
608
- },
609
- ],
610
- };
611
- }
612
- }
613
-
614
- private getVersion() {
615
- return {
616
- content: [
617
- {
618
- type: "text",
619
- text: JSON.stringify({
620
- version: "1.0.4",
621
- name: "azure-mcp-server",
622
- }, null, 2),
623
- },
624
- ],
625
- };
626
- }
627
-
628
- private async executeStoredProcedure(args: { server: string; database: string; procedureName: string; username: string; parameters?: any; password?: string }) {
629
- try {
630
- const pool = await this.getSqlPool(args.server, args.database, args.username, args.password);
631
- const request = pool.request();
632
-
633
- // Add parameters if provided
634
- if (args.parameters) {
635
- for (const [key, value] of Object.entries(args.parameters)) {
636
- request.input(key, value);
637
- }
638
- }
639
-
640
- const result = await request.execute(args.procedureName);
641
-
642
- return {
643
- content: [
644
- {
645
- type: "text",
646
- text: JSON.stringify({
647
- rowsAffected: result.rowsAffected,
648
- recordset: result.recordset,
649
- recordsets: result.recordsets,
650
- returnValue: result.returnValue,
651
- }, null, 2),
652
- },
653
- ],
654
- };
655
- } catch (error: any) {
656
- return {
657
- content: [
658
- {
659
- type: "text",
660
- text: `Stored procedure execution failed: ${error.message}`,
661
- },
662
- ],
663
- };
664
- }
665
- }
666
-
667
- async run() {
668
- const transport = new StdioServerTransport();
669
- await this.server.connect(transport);
670
- console.error("Azure MCP Server running on stdio");
671
- }
672
- }
673
-
674
- // Start the server
675
- const server = new AzureMCPServer();
676
- server.run().catch(console.error);