@dynatrace-oss/dynatrace-mcp-server 0.9.0 → 0.9.1
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.
|
@@ -80,7 +80,8 @@ const createOAuthClientCredentialsHttpClient = async (environmentUrl, scopes, cl
|
|
|
80
80
|
};
|
|
81
81
|
/** Create an OAuth Client using authorization code flow (interactive authentication)
|
|
82
82
|
* This starts a local HTTP server to handle the OAuth redirect and requires user interaction.
|
|
83
|
-
* Implements token
|
|
83
|
+
* Implements an in-memory token cache (not persisted to disk). After every server restart a new
|
|
84
|
+
* authentication flow (or token refresh) may be required.
|
|
84
85
|
* Note: Always requests a complete set of scopes for maximum token reusability. Else the user will end up having to approve multiple requests.
|
|
85
86
|
*/
|
|
86
87
|
const createOAuthAuthCodeFlowHttpClient = async (environmentUrl, scopes, clientId) => {
|
|
@@ -1,149 +1,54 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.globalTokenCache = exports.
|
|
37
|
-
const fs = __importStar(require("fs"));
|
|
38
|
-
const path = __importStar(require("path"));
|
|
3
|
+
exports.globalTokenCache = exports.InMemoryTokenCache = void 0;
|
|
39
4
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
5
|
+
* In-memory token cache implementation (no persistence across process restarts).
|
|
6
|
+
* The previous implementation stored tokens on disk in `.dt-mcp/token.json` – this has been
|
|
7
|
+
* intentionally removed to avoid writing credentials to the local filesystem. A new login /
|
|
8
|
+
* OAuth authorization code flow (or token retrieval) will be required after every server restart.
|
|
42
9
|
*/
|
|
43
|
-
class
|
|
44
|
-
tokenFilePath;
|
|
10
|
+
class InMemoryTokenCache {
|
|
45
11
|
token = null;
|
|
46
|
-
constructor() {
|
|
47
|
-
// Create .dt-mcp directory in the current working directory
|
|
48
|
-
const tokenDir = path.join(process.cwd(), '.dt-mcp');
|
|
49
|
-
this.tokenFilePath = path.join(tokenDir, 'token.json');
|
|
50
|
-
// Ensure the directory exists
|
|
51
|
-
if (!fs.existsSync(tokenDir)) {
|
|
52
|
-
fs.mkdirSync(tokenDir, { recursive: true });
|
|
53
|
-
}
|
|
54
|
-
this.loadToken();
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Loads the token from the file system
|
|
58
|
-
*/
|
|
59
|
-
loadToken() {
|
|
60
|
-
try {
|
|
61
|
-
if (fs.existsSync(this.tokenFilePath)) {
|
|
62
|
-
const tokenData = fs.readFileSync(this.tokenFilePath, 'utf8');
|
|
63
|
-
this.token = JSON.parse(tokenData);
|
|
64
|
-
console.error(`🔍 Loaded token from file: ${this.tokenFilePath}`);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
console.error(`🔍 No token file found at: ${this.tokenFilePath}`);
|
|
68
|
-
this.token = null;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
catch (error) {
|
|
72
|
-
console.error(`❌ Failed to load token from file: ${error}`);
|
|
73
|
-
this.token = null;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Saves the token to the file system
|
|
78
|
-
*/
|
|
79
|
-
saveToken() {
|
|
80
|
-
try {
|
|
81
|
-
if (this.token) {
|
|
82
|
-
fs.writeFileSync(this.tokenFilePath, JSON.stringify(this.token, null, 2), 'utf8');
|
|
83
|
-
console.error(`✅ Saved token to file: ${this.tokenFilePath}`);
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
// Remove the file if no token exists
|
|
87
|
-
if (fs.existsSync(this.tokenFilePath)) {
|
|
88
|
-
fs.unlinkSync(this.tokenFilePath);
|
|
89
|
-
console.error(`🗑️ Removed token file: ${this.tokenFilePath}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
console.error(`❌ Failed to save token to file: ${error}`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
12
|
/**
|
|
98
13
|
* Retrieves the cached token (ignores scopes since we use a global token)
|
|
99
14
|
*/
|
|
100
15
|
getToken(scopes) {
|
|
101
|
-
//
|
|
16
|
+
// Scopes parameter ignored – single global token covers all requested scopes.
|
|
102
17
|
return this.token;
|
|
103
18
|
}
|
|
104
19
|
/**
|
|
105
20
|
* Stores the global token in the cache and persists it to file
|
|
106
21
|
*/
|
|
107
22
|
setToken(scopes, token) {
|
|
108
|
-
// We ignore the scopes parameter since we use a single token with all scopes
|
|
109
23
|
this.token = {
|
|
110
24
|
access_token: token.access_token,
|
|
111
25
|
refresh_token: token.refresh_token,
|
|
112
26
|
expires_at: token.expires_in ? Date.now() + token.expires_in * 1000 : undefined,
|
|
113
|
-
scopes: [...scopes],
|
|
27
|
+
scopes: [...scopes],
|
|
114
28
|
};
|
|
115
|
-
this.saveToken();
|
|
116
29
|
}
|
|
117
30
|
/**
|
|
118
31
|
* Removes the cached token and deletes the file
|
|
119
32
|
*/
|
|
120
33
|
clearToken(scopes) {
|
|
121
|
-
// We ignore the scopes parameter since we use a single global token
|
|
122
34
|
this.token = null;
|
|
123
|
-
this.saveToken();
|
|
124
35
|
}
|
|
125
36
|
/**
|
|
126
37
|
* Checks if the token exists and is still valid (not expired)
|
|
127
38
|
*/
|
|
128
39
|
isTokenValid(scopes) {
|
|
129
40
|
// We ignore the scopes parameter since we use a single token with all scopes
|
|
130
|
-
if (!this.token)
|
|
131
|
-
console.error(`🔍 Token validation: No token in cache`);
|
|
41
|
+
if (!this.token)
|
|
132
42
|
return false;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (!this.token.expires_at) {
|
|
136
|
-
console.error(`🔍 Token validation: Token has no expiration, assuming valid`);
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
43
|
+
if (!this.token.expires_at)
|
|
44
|
+
return true; // treat as non-expiring
|
|
139
45
|
// Add a 30-second buffer to avoid using tokens that are about to expire
|
|
140
46
|
const bufferMs = 30 * 1000; // 30 seconds
|
|
141
47
|
const now = Date.now();
|
|
142
48
|
const expiresAt = this.token.expires_at;
|
|
143
|
-
|
|
144
|
-
return isValid;
|
|
49
|
+
return now + bufferMs < expiresAt;
|
|
145
50
|
}
|
|
146
51
|
}
|
|
147
|
-
exports.
|
|
148
|
-
// Global token cache instance -
|
|
149
|
-
exports.globalTokenCache = new
|
|
52
|
+
exports.InMemoryTokenCache = InMemoryTokenCache;
|
|
53
|
+
// Global token cache instance - In-memory only
|
|
54
|
+
exports.globalTokenCache = new InMemoryTokenCache();
|