kern0-cli 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/README.md +110 -0
- package/README.npm.md +137 -0
- package/bin/kern0 +23 -0
- package/bin/kern0-mcp +2 -0
- package/lib/cli/api.ts +208 -0
- package/lib/cli/auth-flow.ts +155 -0
- package/lib/cli/commands/auth.ts +126 -0
- package/lib/cli/commands/customers.ts +68 -0
- package/lib/cli/commands/environments.ts +74 -0
- package/lib/cli/commands/org.ts +130 -0
- package/lib/cli/commands/tokens.ts +124 -0
- package/lib/cli/config.ts +257 -0
- package/lib/cli/index.ts +222 -0
- package/lib/mcp/api-client.ts +76 -0
- package/lib/mcp/resources.ts +55 -0
- package/lib/mcp/server.ts +216 -0
- package/lib/mcp/tools.ts +288 -0
- package/package.json +76 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
|
|
5
|
+
export interface Auth {
|
|
6
|
+
device_token: string;
|
|
7
|
+
expires_at: number;
|
|
8
|
+
token_type: "device" | "pat";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface User {
|
|
12
|
+
id: string;
|
|
13
|
+
email: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface Organization {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Config {
|
|
22
|
+
api_url: string;
|
|
23
|
+
current_org_id?: string;
|
|
24
|
+
current_customer_id?: string;
|
|
25
|
+
current_env_id?: string;
|
|
26
|
+
user?: User;
|
|
27
|
+
auth?: Auth;
|
|
28
|
+
organizations?: Organization[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const CONFIG_DIR = path.join(os.homedir(), ".kern0");
|
|
32
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
33
|
+
const CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials");
|
|
34
|
+
|
|
35
|
+
export function ensureConfigDir(): void {
|
|
36
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
37
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function loadConfig(): Config {
|
|
42
|
+
ensureConfigDir();
|
|
43
|
+
|
|
44
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
45
|
+
return { api_url: process.env.KERN0_API_URL || "http://localhost:3000" };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const content = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
50
|
+
return JSON.parse(content);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("Failed to load config:", error);
|
|
53
|
+
return { api_url: process.env.KERN0_API_URL || "http://localhost:3000" };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function saveConfig(config: Config): void {
|
|
58
|
+
ensureConfigDir();
|
|
59
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), {
|
|
60
|
+
mode: 0o600,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function loadToken(): string | null {
|
|
65
|
+
ensureConfigDir();
|
|
66
|
+
|
|
67
|
+
if (!fs.existsSync(CREDENTIALS_FILE)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const token = fs.readFileSync(CREDENTIALS_FILE, "utf-8").trim();
|
|
73
|
+
return token || null;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function saveToken(token: string): void {
|
|
80
|
+
ensureConfigDir();
|
|
81
|
+
fs.writeFileSync(CREDENTIALS_FILE, token, { mode: 0o600 });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function deleteToken(): void {
|
|
85
|
+
ensureConfigDir();
|
|
86
|
+
if (fs.existsSync(CREDENTIALS_FILE)) {
|
|
87
|
+
fs.unlinkSync(CREDENTIALS_FILE);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function getCurrentOrg(): string | null {
|
|
92
|
+
const config = loadConfig();
|
|
93
|
+
return config.current_org_id || null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function setCurrentOrg(orgId: string): void {
|
|
97
|
+
const config = loadConfig();
|
|
98
|
+
config.current_org_id = orgId;
|
|
99
|
+
config.current_customer_id = undefined; // Clear customer when switching org
|
|
100
|
+
saveConfig(config);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getCurrentCustomer(): string | null {
|
|
104
|
+
const config = loadConfig();
|
|
105
|
+
return config.current_customer_id || null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function setCurrentCustomer(customerId: string): void {
|
|
109
|
+
const config = loadConfig();
|
|
110
|
+
config.current_customer_id = customerId;
|
|
111
|
+
saveConfig(config);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function getCurrentEnv(): string | null {
|
|
115
|
+
const config = loadConfig();
|
|
116
|
+
return config.current_env_id || null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function setCurrentEnv(envId: string): void {
|
|
120
|
+
const config = loadConfig();
|
|
121
|
+
config.current_env_id = envId;
|
|
122
|
+
saveConfig(config);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function getUser(): User | null {
|
|
126
|
+
const config = loadConfig();
|
|
127
|
+
return config.user || null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function setUser(user: User): void {
|
|
131
|
+
const config = loadConfig();
|
|
132
|
+
config.user = user;
|
|
133
|
+
saveConfig(config);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function getOrganizations(): Organization[] {
|
|
137
|
+
const config = loadConfig();
|
|
138
|
+
return config.organizations || [];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function setOrganizations(orgs: Organization[]): void {
|
|
142
|
+
const config = loadConfig();
|
|
143
|
+
config.organizations = orgs;
|
|
144
|
+
saveConfig(config);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function addOrganization(org: Organization): void {
|
|
148
|
+
const config = loadConfig();
|
|
149
|
+
const orgs = config.organizations || [];
|
|
150
|
+
|
|
151
|
+
// Don't add duplicate
|
|
152
|
+
if (!orgs.find(o => o.id === org.id)) {
|
|
153
|
+
orgs.push(org);
|
|
154
|
+
config.organizations = orgs;
|
|
155
|
+
saveConfig(config);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function getAuth(): Auth | null {
|
|
160
|
+
const config = loadConfig();
|
|
161
|
+
return config.auth || null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function setAuth(auth: Auth): void {
|
|
165
|
+
const config = loadConfig();
|
|
166
|
+
config.auth = auth;
|
|
167
|
+
saveConfig(config);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function isAuthenticated(): boolean {
|
|
171
|
+
const token = loadToken();
|
|
172
|
+
const auth = getAuth();
|
|
173
|
+
|
|
174
|
+
if (!token || !auth) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Check if token expired
|
|
179
|
+
if (auth.expires_at && auth.expires_at < Date.now()) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function requireAuthentication(): string {
|
|
187
|
+
if (!isAuthenticated()) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
"Not authenticated. Run `kern0 login` to authenticate."
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const token = loadToken();
|
|
194
|
+
if (!token) {
|
|
195
|
+
throw new Error("Token not found. Run `kern0 login` to authenticate.");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return token;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function requireOrganization(): string {
|
|
202
|
+
const orgId = getCurrentOrg();
|
|
203
|
+
if (!orgId) {
|
|
204
|
+
throw new Error(
|
|
205
|
+
"No organization selected. Run `kern0 org select <orgId>` to select one."
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
return orgId;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function requireCustomer(): string {
|
|
212
|
+
const customerId = getCurrentCustomer();
|
|
213
|
+
if (!customerId) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
"No customer selected. Run `kern0 switch-customer <id>` to select one."
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return customerId;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function printContext(): void {
|
|
222
|
+
const config = loadConfig();
|
|
223
|
+
const auth = getAuth();
|
|
224
|
+
const user = getUser();
|
|
225
|
+
const org = config.current_org_id
|
|
226
|
+
? getOrganizations().find(o => o.id === config.current_org_id)
|
|
227
|
+
: null;
|
|
228
|
+
|
|
229
|
+
console.log("\n📋 Current Context:");
|
|
230
|
+
|
|
231
|
+
if (user) {
|
|
232
|
+
console.log(` User: ${user.email} (${user.id})`);
|
|
233
|
+
} else {
|
|
234
|
+
console.log(` User: Not authenticated`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (org) {
|
|
238
|
+
console.log(` Organization: ${org.name} (${org.id})`);
|
|
239
|
+
} else {
|
|
240
|
+
console.log(` Organization: None selected`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (config.current_customer_id) {
|
|
244
|
+
console.log(` Customer: ${config.current_customer_id}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (config.current_env_id) {
|
|
248
|
+
console.log(` Environment: ${config.current_env_id}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (auth?.expires_at) {
|
|
252
|
+
const expiresIn = Math.floor((auth.expires_at - Date.now()) / 1000 / 60 / 60);
|
|
253
|
+
console.log(` Token expires: ${expiresIn} hours from now`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
console.log("");
|
|
257
|
+
}
|
package/lib/cli/index.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { login, logout } from "./commands/auth";
|
|
4
|
+
import {
|
|
5
|
+
listOrgs,
|
|
6
|
+
selectOrg,
|
|
7
|
+
getOrgInfo,
|
|
8
|
+
createOrg,
|
|
9
|
+
} from "./commands/org";
|
|
10
|
+
import {
|
|
11
|
+
listTokensCmd,
|
|
12
|
+
createTokenCmd,
|
|
13
|
+
revokeTokenCmd,
|
|
14
|
+
} from "./commands/tokens";
|
|
15
|
+
import {
|
|
16
|
+
listCustomersCmd,
|
|
17
|
+
switchCustomerCmd,
|
|
18
|
+
} from "./commands/customers";
|
|
19
|
+
import {
|
|
20
|
+
listEnvironments,
|
|
21
|
+
getEnvironmentInfo,
|
|
22
|
+
} from "./commands/environments";
|
|
23
|
+
import { printContext } from "./config";
|
|
24
|
+
|
|
25
|
+
const args = process.argv.slice(2);
|
|
26
|
+
const command = args[0];
|
|
27
|
+
const subcommand = args[1];
|
|
28
|
+
const arg1 = args[2];
|
|
29
|
+
const arg2 = args[3];
|
|
30
|
+
|
|
31
|
+
async function main() {
|
|
32
|
+
try {
|
|
33
|
+
switch (command) {
|
|
34
|
+
// Auth commands
|
|
35
|
+
case "login":
|
|
36
|
+
await login();
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
case "logout":
|
|
40
|
+
await logout();
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
// Organization commands
|
|
44
|
+
case "org":
|
|
45
|
+
switch (subcommand) {
|
|
46
|
+
case "list":
|
|
47
|
+
await listOrgs();
|
|
48
|
+
break;
|
|
49
|
+
case "select":
|
|
50
|
+
if (!arg1) {
|
|
51
|
+
console.error("Usage: kern0 org select <orgId>");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
await selectOrg(arg1);
|
|
55
|
+
break;
|
|
56
|
+
case "info":
|
|
57
|
+
await getOrgInfo();
|
|
58
|
+
break;
|
|
59
|
+
case "create":
|
|
60
|
+
if (!arg1) {
|
|
61
|
+
console.error("Usage: kern0 org create <name> [type]");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const type = (arg2 as "direct" | "msp") || "direct";
|
|
65
|
+
await createOrg(arg1, type);
|
|
66
|
+
break;
|
|
67
|
+
default:
|
|
68
|
+
console.error("Usage: kern0 org [list|select|info|create]");
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
// Customer commands
|
|
74
|
+
case "customers":
|
|
75
|
+
switch (subcommand) {
|
|
76
|
+
case "list":
|
|
77
|
+
await listCustomersCmd();
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
console.error("Usage: kern0 customers list");
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
|
|
85
|
+
case "switch-customer":
|
|
86
|
+
if (!arg1) {
|
|
87
|
+
console.error("Usage: kern0 switch-customer <customerId>");
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
await switchCustomerCmd(arg1);
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
// Token commands
|
|
94
|
+
case "tokens":
|
|
95
|
+
switch (subcommand) {
|
|
96
|
+
case "list":
|
|
97
|
+
await listTokensCmd();
|
|
98
|
+
break;
|
|
99
|
+
case "create":
|
|
100
|
+
if (!arg1) {
|
|
101
|
+
console.error("Usage: kern0 tokens create <name>");
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
await createTokenCmd(arg1);
|
|
105
|
+
break;
|
|
106
|
+
case "delete":
|
|
107
|
+
case "revoke":
|
|
108
|
+
if (!arg1) {
|
|
109
|
+
console.error("Usage: kern0 tokens revoke <tokenId>");
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
await revokeTokenCmd(arg1);
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
console.error("Usage: kern0 tokens [list|create|revoke]");
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
|
|
120
|
+
// Environment commands
|
|
121
|
+
case "environments":
|
|
122
|
+
case "envs":
|
|
123
|
+
case "env":
|
|
124
|
+
switch (subcommand) {
|
|
125
|
+
case "list":
|
|
126
|
+
await listEnvironments();
|
|
127
|
+
break;
|
|
128
|
+
case "info":
|
|
129
|
+
if (!arg1) {
|
|
130
|
+
console.error("Usage: kern0 environments info <envId>");
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
await getEnvironmentInfo(arg1);
|
|
134
|
+
break;
|
|
135
|
+
default:
|
|
136
|
+
console.error("Usage: kern0 environments [list|info <envId>]");
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
|
|
141
|
+
// Status commands
|
|
142
|
+
case "whoami":
|
|
143
|
+
printContext();
|
|
144
|
+
break;
|
|
145
|
+
|
|
146
|
+
case "--version":
|
|
147
|
+
case "-v":
|
|
148
|
+
console.log("Kern0 CLI v0.1.0");
|
|
149
|
+
break;
|
|
150
|
+
|
|
151
|
+
case "--help":
|
|
152
|
+
case "-h":
|
|
153
|
+
case undefined:
|
|
154
|
+
showHelp();
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
default:
|
|
158
|
+
console.error(`Unknown command: ${command}`);
|
|
159
|
+
console.error("Run 'kern0 --help' for usage information");
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (error instanceof Error) {
|
|
164
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
165
|
+
}
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function showHelp() {
|
|
171
|
+
console.log(`
|
|
172
|
+
Kern0 CLI - Recovery Readiness Assessment Tool
|
|
173
|
+
|
|
174
|
+
USAGE:
|
|
175
|
+
kern0 <command> [options]
|
|
176
|
+
|
|
177
|
+
AUTHENTICATION:
|
|
178
|
+
login Authenticate with OAuth device flow
|
|
179
|
+
logout Clear credentials and log out
|
|
180
|
+
whoami Show current user, organization, and customer
|
|
181
|
+
|
|
182
|
+
ORGANIZATIONS:
|
|
183
|
+
org list List your organizations
|
|
184
|
+
org select <id> Switch to an organization
|
|
185
|
+
org info Show current organization details
|
|
186
|
+
org create <name> Create a new organization
|
|
187
|
+
|
|
188
|
+
CUSTOMERS:
|
|
189
|
+
customers list List customers in current organization
|
|
190
|
+
switch-customer <id> Switch to a customer
|
|
191
|
+
|
|
192
|
+
TOKENS:
|
|
193
|
+
tokens list List API tokens
|
|
194
|
+
tokens create <name> Create a new API token
|
|
195
|
+
tokens revoke <id> Revoke an API token
|
|
196
|
+
|
|
197
|
+
CONFIG:
|
|
198
|
+
config get <key> Get a configuration value
|
|
199
|
+
config set <key> <val> Set a configuration value
|
|
200
|
+
|
|
201
|
+
OPTIONS:
|
|
202
|
+
-h, --help Show this help message
|
|
203
|
+
-v, --version Show version
|
|
204
|
+
|
|
205
|
+
EXAMPLES:
|
|
206
|
+
kern0 login
|
|
207
|
+
kern0 org list
|
|
208
|
+
kern0 org select acme-corp
|
|
209
|
+
kern0 customers list
|
|
210
|
+
kern0 switch-customer acme-prod
|
|
211
|
+
kern0 tokens create "GitHub Actions"
|
|
212
|
+
kern0 whoami
|
|
213
|
+
|
|
214
|
+
For more information, visit: https://kern0.dev/docs/cli
|
|
215
|
+
`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Run CLI
|
|
219
|
+
main().catch(error => {
|
|
220
|
+
console.error("Fatal error:", error);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API client for MCP server to communicate with KernZero backend
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export async function fetchAPI(
|
|
6
|
+
endpoint: string,
|
|
7
|
+
token: string,
|
|
8
|
+
apiUrl: string
|
|
9
|
+
): Promise<any> {
|
|
10
|
+
const url = new URL(endpoint, apiUrl).href;
|
|
11
|
+
|
|
12
|
+
const response = await fetch(url, {
|
|
13
|
+
method: "GET",
|
|
14
|
+
headers: {
|
|
15
|
+
"Authorization": `Bearer ${token}`,
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
const errorText = await response.text();
|
|
22
|
+
throw new Error(
|
|
23
|
+
`API request failed: ${response.status} ${response.statusText}\n${errorText}`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const data = await response.json();
|
|
28
|
+
|
|
29
|
+
// Handle wrapped API responses
|
|
30
|
+
if (data.success && data.data) {
|
|
31
|
+
return data.data;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Handle direct responses (auth/me endpoint)
|
|
35
|
+
if (data.authenticated !== undefined) {
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Return as-is for other responses
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function callAPI(
|
|
44
|
+
method: "GET" | "POST" | "PUT" | "DELETE",
|
|
45
|
+
endpoint: string,
|
|
46
|
+
token: string,
|
|
47
|
+
apiUrl: string,
|
|
48
|
+
body?: any
|
|
49
|
+
): Promise<any> {
|
|
50
|
+
const url = new URL(endpoint, apiUrl).href;
|
|
51
|
+
|
|
52
|
+
const response = await fetch(url, {
|
|
53
|
+
method,
|
|
54
|
+
headers: {
|
|
55
|
+
"Authorization": `Bearer ${token}`,
|
|
56
|
+
"Content-Type": "application/json",
|
|
57
|
+
},
|
|
58
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const errorText = await response.text();
|
|
63
|
+
throw new Error(
|
|
64
|
+
`API request failed: ${response.status} ${response.statusText}\n${errorText}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const data = await response.json();
|
|
69
|
+
|
|
70
|
+
// Handle wrapped API responses
|
|
71
|
+
if (data.success && data.data) {
|
|
72
|
+
return data.data;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return data;
|
|
76
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP resource definitions for KernZero
|
|
3
|
+
* Resources represent data that Claude can read and analyze
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface Resource {
|
|
7
|
+
uri: string;
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
mimeType: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getOrganizationResources(org: any): Resource[] {
|
|
14
|
+
return [
|
|
15
|
+
{
|
|
16
|
+
uri: `kernzero://organization/${org.id}`,
|
|
17
|
+
name: `Organization: ${org.name}`,
|
|
18
|
+
description: `Details for ${org.name} (${org.type} type) - Created ${new Date(org.createdAt).toLocaleDateString()}`,
|
|
19
|
+
mimeType: "application/json",
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getEnvironmentResources(orgId: string): Resource[] {
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
27
|
+
uri: `kernzero://environments/${orgId}`,
|
|
28
|
+
name: `Environments in ${orgId}`,
|
|
29
|
+
description: `All monitored environments and forest DNS for organization ${orgId}`,
|
|
30
|
+
mimeType: "application/json",
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getTokenResources(orgId: string): Resource[] {
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
uri: `kernzero://tokens/${orgId}`,
|
|
39
|
+
name: `API Tokens in ${orgId}`,
|
|
40
|
+
description: `Personal Access Tokens and their scopes for organization ${orgId}`,
|
|
41
|
+
mimeType: "application/json",
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getAuditLogResources(orgId: string): Resource[] {
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
uri: `kernzero://audit-logs/${orgId}`,
|
|
50
|
+
name: `Audit Logs in ${orgId}`,
|
|
51
|
+
description: `Action history and who did what when in organization ${orgId}`,
|
|
52
|
+
mimeType: "application/json",
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
}
|