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 +127 -0
- package/index.js +70 -69
- package/index.min.js +1 -1
- package/package.json +1 -1
- package/index - Copy.js +0 -19
- package/junk.js +0 -676
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.
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
var
|
|
104
|
-
retVal
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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.
|
|
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
|
-
|
|
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
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);
|