@prajwolkc/stk 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -158,6 +158,53 @@ GITHUB_TOKEN=
158
158
  GITHUB_REPO=owner/repo # or auto-detected from git remote
159
159
  ```
160
160
 
161
+ ## Claude Code / MCP Integration
162
+
163
+ `stk` ships with a built-in MCP server so Claude Code can use your infrastructure as native tools.
164
+
165
+ ### Setup
166
+
167
+ 1. Install stk globally:
168
+
169
+ ```bash
170
+ npm install -g @prajwolkc/stk
171
+ ```
172
+
173
+ 2. Add to your project's `.mcp.json` (create it in your project root):
174
+
175
+ ```json
176
+ {
177
+ "mcpServers": {
178
+ "stk": {
179
+ "command": "stk-mcp",
180
+ "args": []
181
+ }
182
+ }
183
+ }
184
+ ```
185
+
186
+ 3. Restart Claude Code. Approve the stk MCP server when prompted.
187
+
188
+ ### What Claude can do
189
+
190
+ | Tool | Description |
191
+ |------|-------------|
192
+ | `stk_health` | Check if all services are up before writing code |
193
+ | `stk_status` | Full overview: git, services, deploys, issues |
194
+ | `stk_doctor` | Diagnose misconfig and missing env vars |
195
+ | `stk_logs` | Read production logs to understand bugs |
196
+ | `stk_todo_list` | See what needs to be worked on |
197
+ | `stk_todo_add` | Create GitHub issues |
198
+ | `stk_deploy` | Push code and trigger deploys |
199
+ | `stk_config` | Read the project's stack config |
200
+
201
+ ### Example prompts
202
+
203
+ - *"Check if all my services are healthy"*
204
+ - *"What errors are in my production logs?"*
205
+ - *"What should I work on next?"*
206
+ - *"Deploy this and verify it worked"*
207
+
161
208
  ## Development
162
209
 
163
210
  ```bash
@@ -3,6 +3,7 @@ import chalk from "chalk";
3
3
  import ora from "ora";
4
4
  import { loadConfig, enabledServices } from "../lib/config.js";
5
5
  import { getChecker, allCheckerNames, loadPluginCheckers } from "../services/registry.js";
6
+ import { jsonOutput } from "../lib/output.js";
6
7
  const STATUS_ICON = {
7
8
  healthy: chalk.green("✓"),
8
9
  degraded: chalk.yellow("~"),
@@ -19,21 +20,25 @@ export const healthCommand = new Command("health")
19
20
  .description("Check the health of all connected services")
20
21
  .option("-v, --verbose", "Show latency and extra detail")
21
22
  .option("-a, --all", "Check all known services, not just configured ones")
23
+ .option("-j, --json", "Output as JSON")
22
24
  .action(async (opts) => {
23
25
  const config = loadConfig();
24
- const spinner = ora("Checking services...").start();
25
26
  await loadPluginCheckers();
26
27
  const serviceList = opts.all
27
28
  ? allCheckerNames()
28
29
  : enabledServices(config);
29
30
  if (serviceList.length === 0) {
30
- spinner.stop();
31
+ if (opts.json) {
32
+ jsonOutput({ services: [], summary: "no services configured" });
33
+ return;
34
+ }
31
35
  console.log();
32
36
  console.log(chalk.yellow(" No services configured."));
33
37
  console.log(chalk.dim(` Run ${chalk.white("stk init")} to set up your project, or ${chalk.white("stk health --all")} to check everything.`));
34
38
  console.log();
35
39
  return;
36
40
  }
41
+ const spinner = opts.json ? null : ora("Checking services...").start();
37
42
  const checks = serviceList.map((name) => {
38
43
  const checker = getChecker(name);
39
44
  if (!checker) {
@@ -46,7 +51,26 @@ export const healthCommand = new Command("health")
46
51
  return checker();
47
52
  });
48
53
  const results = await Promise.all(checks);
49
- spinner.stop();
54
+ spinner?.stop();
55
+ // JSON output
56
+ if (opts.json) {
57
+ const down = results.filter((r) => r.status === "down");
58
+ jsonOutput({
59
+ project: config.name,
60
+ services: results,
61
+ summary: {
62
+ healthy: results.filter((r) => r.status === "healthy").length,
63
+ down: down.length,
64
+ skipped: results.filter((r) => r.status === "skipped").length,
65
+ total: results.length,
66
+ },
67
+ ok: down.length === 0,
68
+ });
69
+ if (down.length > 0)
70
+ process.exitCode = 1;
71
+ return;
72
+ }
73
+ // Human output
50
74
  console.log();
51
75
  console.log(chalk.bold(` ${config.name} — Service Health`));
52
76
  console.log(chalk.dim(" ─────────────────────────────────────────"));
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Shared output helper. When --json is passed, commands collect data
3
+ * and call jsonOutput() instead of console.log with chalk.
4
+ */
5
+ export declare function setJsonMode(enabled: boolean): void;
6
+ export declare function isJsonMode(): boolean;
7
+ export declare function jsonOutput(data: unknown): void;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Shared output helper. When --json is passed, commands collect data
3
+ * and call jsonOutput() instead of console.log with chalk.
4
+ */
5
+ let jsonMode = false;
6
+ export function setJsonMode(enabled) {
7
+ jsonMode = enabled;
8
+ }
9
+ export function isJsonMode() {
10
+ return jsonMode;
11
+ }
12
+ export function jsonOutput(data) {
13
+ console.log(JSON.stringify(data, null, 2));
14
+ }
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * stk MCP Server
4
+ *
5
+ * Exposes your entire infrastructure as tools for Claude Code.
6
+ * Claude can check health, read logs, deploy, manage issues, and diagnose
7
+ * problems — all through structured tool calls.
8
+ */
9
+ export {};
@@ -0,0 +1,385 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * stk MCP Server
4
+ *
5
+ * Exposes your entire infrastructure as tools for Claude Code.
6
+ * Claude can check health, read logs, deploy, manage issues, and diagnose
7
+ * problems — all through structured tool calls.
8
+ */
9
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
+ import { z } from "zod";
12
+ import { loadConfig, enabledServices } from "../lib/config.js";
13
+ import { getChecker, allCheckerNames, loadPluginCheckers } from "../services/registry.js";
14
+ import { execSync } from "child_process";
15
+ const server = new McpServer({
16
+ name: "stk",
17
+ version: "0.2.0",
18
+ });
19
+ // ──────────────────────────────────────────
20
+ // Tool: stk_health
21
+ // ──────────────────────────────────────────
22
+ server.tool("stk_health", "Check the health of all configured infrastructure services (databases, deploy providers, storage, billing). Returns structured results with status, latency, and details for each service.", {
23
+ all: z.boolean().optional().describe("Check all known services, not just configured ones"),
24
+ }, async ({ all }) => {
25
+ await loadPluginCheckers();
26
+ const config = loadConfig();
27
+ const serviceList = all ? allCheckerNames() : enabledServices(config);
28
+ const checks = serviceList.map(async (name) => {
29
+ const checker = getChecker(name);
30
+ if (!checker) {
31
+ return { name, status: "skipped", detail: `unknown service "${name}"` };
32
+ }
33
+ return checker();
34
+ });
35
+ const results = await Promise.all(checks);
36
+ const down = results.filter((r) => r.status === "down");
37
+ return {
38
+ content: [
39
+ {
40
+ type: "text",
41
+ text: JSON.stringify({
42
+ project: config.name,
43
+ services: results,
44
+ summary: {
45
+ healthy: results.filter((r) => r.status === "healthy").length,
46
+ down: down.length,
47
+ skipped: results.filter((r) => r.status === "skipped").length,
48
+ total: results.length,
49
+ },
50
+ ok: down.length === 0,
51
+ }, null, 2),
52
+ },
53
+ ],
54
+ };
55
+ });
56
+ // ──────────────────────────────────────────
57
+ // Tool: stk_status
58
+ // ──────────────────────────────────────────
59
+ server.tool("stk_status", "Get a complete status overview: git state, service health, last deploy, and open issues — everything in one call.", {}, async () => {
60
+ const config = loadConfig();
61
+ const status = { project: config.name };
62
+ // Git
63
+ try {
64
+ status.git = {
65
+ branch: execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim(),
66
+ dirty: execSync("git status --porcelain", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n").filter(Boolean).length,
67
+ lastCommit: execSync('git log -1 --format="%s"', { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim(),
68
+ lastCommitAge: execSync('git log -1 --format="%cr"', { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim(),
69
+ };
70
+ }
71
+ catch {
72
+ status.git = null;
73
+ }
74
+ // Services
75
+ await loadPluginCheckers();
76
+ const serviceList = enabledServices(config);
77
+ if (serviceList.length > 0) {
78
+ const checks = serviceList.map(async (name) => {
79
+ const checker = getChecker(name);
80
+ if (!checker)
81
+ return { name, status: "skipped" };
82
+ return checker();
83
+ });
84
+ const results = await Promise.all(checks);
85
+ status.services = {
86
+ healthy: results.filter((r) => r.status === "healthy").length,
87
+ down: results.filter((r) => r.status === "down").map((r) => r.name),
88
+ skipped: results.filter((r) => r.status === "skipped").length,
89
+ total: results.length,
90
+ };
91
+ }
92
+ else {
93
+ status.services = { total: 0, note: "no services configured" };
94
+ }
95
+ // Deploy
96
+ if (process.env.VERCEL_TOKEN) {
97
+ try {
98
+ const res = await fetch("https://api.vercel.com/v6/deployments?limit=1", {
99
+ headers: { Authorization: `Bearer ${process.env.VERCEL_TOKEN}` },
100
+ });
101
+ const data = (await res.json());
102
+ const dep = data.deployments?.[0];
103
+ if (dep) {
104
+ status.lastDeploy = {
105
+ provider: "vercel",
106
+ state: dep.readyState ?? dep.state,
107
+ url: dep.url,
108
+ created: dep.created,
109
+ };
110
+ }
111
+ }
112
+ catch { /* skip */ }
113
+ }
114
+ return {
115
+ content: [{ type: "text", text: JSON.stringify(status, null, 2) }],
116
+ };
117
+ });
118
+ // ──────────────────────────────────────────
119
+ // Tool: stk_doctor
120
+ // ──────────────────────────────────────────
121
+ server.tool("stk_doctor", "Diagnose infrastructure configuration issues. Checks for missing env vars, mismatched config, invalid URLs, and suggests fixes with documentation links.", {}, async () => {
122
+ const config = loadConfig();
123
+ const enabled = enabledServices(config);
124
+ const issues = [];
125
+ const ENV_REQS = {
126
+ railway: { required: ["RAILWAY_API_TOKEN"], optional: ["RAILWAY_PROJECT_ID", "RAILWAY_ENVIRONMENT_ID", "RAILWAY_SERVICE_ID"] },
127
+ vercel: { required: ["VERCEL_TOKEN"], optional: ["VERCEL_PROJECT_ID"] },
128
+ fly: { required: ["FLY_API_TOKEN"], optional: ["FLY_APP_NAME"] },
129
+ render: { required: ["RENDER_API_KEY"], optional: [] },
130
+ aws: { required: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], optional: ["AWS_REGION"] },
131
+ database: { required: ["DATABASE_URL"], optional: [] },
132
+ mongodb: { required: ["MONGODB_URL"], optional: [] },
133
+ redis: { required: ["REDIS_URL"], optional: [] },
134
+ supabase: { required: ["SUPABASE_URL"], optional: ["SUPABASE_SERVICE_KEY"] },
135
+ r2: { required: ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN"], optional: [] },
136
+ stripe: { required: ["STRIPE_SECRET_KEY"], optional: [] },
137
+ };
138
+ for (const svc of enabled) {
139
+ const reqs = ENV_REQS[svc];
140
+ if (!reqs)
141
+ continue;
142
+ const missingReq = reqs.required.filter((v) => !process.env[v]);
143
+ const missingOpt = reqs.optional.filter((v) => !process.env[v]);
144
+ if (missingReq.length > 0) {
145
+ issues.push({ level: "error", service: svc, message: `Missing required: ${missingReq.join(", ")}` });
146
+ }
147
+ else {
148
+ issues.push({ level: "ok", service: svc, message: "Configured correctly" });
149
+ }
150
+ if (missingOpt.length > 0) {
151
+ issues.push({ level: "warn", service: svc, message: `Missing optional: ${missingOpt.join(", ")}`, fix: "Needed for logs, env sync, deploy watching" });
152
+ }
153
+ }
154
+ return {
155
+ content: [{
156
+ type: "text",
157
+ text: JSON.stringify({
158
+ project: config.name,
159
+ issues,
160
+ summary: {
161
+ errors: issues.filter((i) => i.level === "error").length,
162
+ warnings: issues.filter((i) => i.level === "warn").length,
163
+ ok: issues.filter((i) => i.level === "ok").length,
164
+ },
165
+ }, null, 2),
166
+ }],
167
+ };
168
+ });
169
+ // ──────────────────────────────────────────
170
+ // Tool: stk_logs
171
+ // ──────────────────────────────────────────
172
+ server.tool("stk_logs", "Fetch recent production logs from Railway, Vercel, or other deploy providers. Useful for diagnosing errors and understanding runtime behavior.", {
173
+ provider: z.enum(["railway", "vercel"]).optional().describe("Which provider to fetch logs from (auto-detects if omitted)"),
174
+ lines: z.number().optional().default(30).describe("Number of log lines to fetch"),
175
+ }, async ({ provider, lines }) => {
176
+ // Railway logs
177
+ if ((provider === "railway" || !provider) && process.env.RAILWAY_API_TOKEN) {
178
+ const token = process.env.RAILWAY_API_TOKEN;
179
+ const projectId = process.env.RAILWAY_PROJECT_ID;
180
+ const serviceId = process.env.RAILWAY_SERVICE_ID;
181
+ if (!projectId) {
182
+ return { content: [{ type: "text", text: JSON.stringify({ error: "RAILWAY_PROJECT_ID not set" }) }] };
183
+ }
184
+ // Get latest deployment
185
+ const serviceFilter = serviceId ? `serviceId: "${serviceId}",` : "";
186
+ const depRes = await fetch("https://backboard.railway.com/graphql/v2", {
187
+ method: "POST",
188
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
189
+ body: JSON.stringify({
190
+ query: `{ deployments(first: 1, input: { projectId: "${projectId}", ${serviceFilter} }) { edges { node { id } } } }`,
191
+ }),
192
+ });
193
+ const depData = (await depRes.json());
194
+ const deploymentId = depData.data?.deployments?.edges?.[0]?.node?.id;
195
+ if (!deploymentId) {
196
+ return { content: [{ type: "text", text: JSON.stringify({ error: "No deployments found" }) }] };
197
+ }
198
+ const logRes = await fetch("https://backboard.railway.com/graphql/v2", {
199
+ method: "POST",
200
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
201
+ body: JSON.stringify({
202
+ query: `{ deploymentLogs(deploymentId: "${deploymentId}", limit: ${lines}) { timestamp message severity } }`,
203
+ }),
204
+ });
205
+ const logData = (await logRes.json());
206
+ const logs = logData.data?.deploymentLogs ?? [];
207
+ return {
208
+ content: [{ type: "text", text: JSON.stringify({ provider: "railway", deploymentId, logs }, null, 2) }],
209
+ };
210
+ }
211
+ // Vercel logs
212
+ if ((provider === "vercel" || !provider) && process.env.VERCEL_TOKEN) {
213
+ const token = process.env.VERCEL_TOKEN;
214
+ const depRes = await fetch("https://api.vercel.com/v6/deployments?limit=1", {
215
+ headers: { Authorization: `Bearer ${token}` },
216
+ });
217
+ const depData = (await depRes.json());
218
+ const dep = depData.deployments?.[0];
219
+ if (!dep) {
220
+ return { content: [{ type: "text", text: JSON.stringify({ error: "No deployments found" }) }] };
221
+ }
222
+ const logRes = await fetch(`https://api.vercel.com/v2/deployments/${dep.uid}/events`, {
223
+ headers: { Authorization: `Bearer ${token}` },
224
+ });
225
+ const events = (await logRes.json());
226
+ const logs = Array.isArray(events)
227
+ ? events
228
+ .filter((e) => e.type === "stdout" || e.type === "stderr")
229
+ .slice(-lines)
230
+ .map((e) => ({
231
+ timestamp: new Date(e.created).toISOString(),
232
+ message: e.payload?.text ?? e.text ?? "",
233
+ severity: e.type === "stderr" ? "ERROR" : "INFO",
234
+ }))
235
+ : [];
236
+ return {
237
+ content: [{ type: "text", text: JSON.stringify({ provider: "vercel", deploymentUrl: dep.url, logs }, null, 2) }],
238
+ };
239
+ }
240
+ return {
241
+ content: [{ type: "text", text: JSON.stringify({ error: "No log provider available. Set RAILWAY_API_TOKEN or VERCEL_TOKEN." }) }],
242
+ };
243
+ });
244
+ // ──────────────────────────────────────────
245
+ // Tool: stk_todo_list
246
+ // ──────────────────────────────────────────
247
+ server.tool("stk_todo_list", "List open GitHub issues for this project. Helps understand what needs to be worked on.", {
248
+ label: z.string().optional().describe("Filter by label"),
249
+ limit: z.number().optional().default(15).describe("Max issues to return"),
250
+ }, async ({ label, limit }) => {
251
+ const config = loadConfig();
252
+ const repo = config.github?.repo ?? process.env.GITHUB_REPO ?? detectGitHubRepo();
253
+ const token = process.env.GITHUB_TOKEN;
254
+ if (!repo) {
255
+ return { content: [{ type: "text", text: JSON.stringify({ error: "Could not detect GitHub repo. Set GITHUB_REPO or add github.repo to stk.config.json" }) }] };
256
+ }
257
+ const params = new URLSearchParams({
258
+ state: "open",
259
+ per_page: String(limit),
260
+ sort: "updated",
261
+ direction: "desc",
262
+ });
263
+ if (label)
264
+ params.set("labels", label);
265
+ const headers = { Accept: "application/vnd.github+json" };
266
+ if (token)
267
+ headers.Authorization = `Bearer ${token}`;
268
+ const res = await fetch(`https://api.github.com/repos/${repo}/issues?${params}`, { headers });
269
+ if (!res.ok) {
270
+ return { content: [{ type: "text", text: JSON.stringify({ error: `GitHub API: ${res.status}` }) }] };
271
+ }
272
+ const issues = (await res.json());
273
+ const filtered = issues
274
+ .filter((i) => !i.pull_request)
275
+ .map((i) => ({
276
+ number: i.number,
277
+ title: i.title,
278
+ labels: i.labels.map((l) => l.name),
279
+ assignee: i.assignee?.login ?? null,
280
+ created: i.created_at,
281
+ url: i.html_url,
282
+ }));
283
+ return {
284
+ content: [{ type: "text", text: JSON.stringify({ repo, issues: filtered }, null, 2) }],
285
+ };
286
+ });
287
+ // ──────────────────────────────────────────
288
+ // Tool: stk_todo_add
289
+ // ──────────────────────────────────────────
290
+ server.tool("stk_todo_add", "Create a new GitHub issue for this project.", {
291
+ title: z.string().describe("Issue title"),
292
+ body: z.string().optional().describe("Issue body/description"),
293
+ labels: z.array(z.string()).optional().describe("Labels to add"),
294
+ }, async ({ title, body, labels }) => {
295
+ const config = loadConfig();
296
+ const repo = config.github?.repo ?? process.env.GITHUB_REPO ?? detectGitHubRepo();
297
+ const token = process.env.GITHUB_TOKEN;
298
+ if (!repo || !token) {
299
+ return { content: [{ type: "text", text: JSON.stringify({ error: "Need GITHUB_TOKEN and repo to create issues" }) }] };
300
+ }
301
+ const payload = { title };
302
+ if (body)
303
+ payload.body = body;
304
+ if (labels)
305
+ payload.labels = labels;
306
+ const res = await fetch(`https://api.github.com/repos/${repo}/issues`, {
307
+ method: "POST",
308
+ headers: {
309
+ Authorization: `Bearer ${token}`,
310
+ Accept: "application/vnd.github+json",
311
+ "Content-Type": "application/json",
312
+ },
313
+ body: JSON.stringify(payload),
314
+ });
315
+ if (!res.ok) {
316
+ const data = (await res.json());
317
+ return { content: [{ type: "text", text: JSON.stringify({ error: data.message ?? `HTTP ${res.status}` }) }] };
318
+ }
319
+ const issue = (await res.json());
320
+ return {
321
+ content: [{ type: "text", text: JSON.stringify({ created: true, number: issue.number, url: issue.html_url }, null, 2) }],
322
+ };
323
+ });
324
+ // ──────────────────────────────────────────
325
+ // Tool: stk_deploy
326
+ // ──────────────────────────────────────────
327
+ server.tool("stk_deploy", "Push current branch to remote and trigger deploys. Use with caution — this pushes code to production.", {
328
+ skipPush: z.boolean().optional().describe("Skip git push, just report current deploy status"),
329
+ }, async ({ skipPush }) => {
330
+ const config = loadConfig();
331
+ const branch = config.deploy?.branch ?? "main";
332
+ if (!skipPush) {
333
+ try {
334
+ const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
335
+ if (currentBranch !== branch) {
336
+ return {
337
+ content: [{ type: "text", text: JSON.stringify({ error: `On branch "${currentBranch}", not "${branch}". Switch branches first.` }) }],
338
+ };
339
+ }
340
+ execSync(`git push origin ${branch}`, { encoding: "utf-8", stdio: "pipe" });
341
+ }
342
+ catch (err) {
343
+ return {
344
+ content: [{ type: "text", text: JSON.stringify({ error: `Git push failed: ${err.message}` }) }],
345
+ };
346
+ }
347
+ }
348
+ return {
349
+ content: [{
350
+ type: "text",
351
+ text: JSON.stringify({
352
+ pushed: !skipPush,
353
+ branch,
354
+ providers: config.deploy?.providers ?? [],
355
+ note: "Deploy triggered. Use stk_health to verify after a few minutes.",
356
+ }, null, 2),
357
+ }],
358
+ };
359
+ });
360
+ // ──────────────────────────────────────────
361
+ // Tool: stk_config
362
+ // ──────────────────────────────────────────
363
+ server.tool("stk_config", "Read the current stk configuration for this project. Shows which services are enabled, deploy settings, and project name.", {}, async () => {
364
+ const config = loadConfig();
365
+ return {
366
+ content: [{ type: "text", text: JSON.stringify(config, null, 2) }],
367
+ };
368
+ });
369
+ // Helper
370
+ function detectGitHubRepo() {
371
+ try {
372
+ const url = execSync("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
373
+ const match = url.match(/github\.com[:/]([^/]+\/[^/.]+)/);
374
+ return match?.[1] ?? null;
375
+ }
376
+ catch {
377
+ return null;
378
+ }
379
+ }
380
+ // Start
381
+ async function main() {
382
+ const transport = new StdioServerTransport();
383
+ await server.connect(transport);
384
+ }
385
+ main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prajwolkc/stk",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "One CLI to deploy, monitor, and debug your entire stack. Health checks, deploy watching, env sync, logs, and GitHub issues — all from one command.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,7 +26,8 @@
26
26
  "developer-tools"
27
27
  ],
28
28
  "bin": {
29
- "stk": "dist/index.js"
29
+ "stk": "dist/index.js",
30
+ "stk-mcp": "dist/mcp/server.js"
30
31
  },
31
32
  "files": [
32
33
  "dist",
@@ -45,9 +46,11 @@
45
46
  "prepublishOnly": "npm run build"
46
47
  },
47
48
  "dependencies": {
49
+ "@modelcontextprotocol/sdk": "^1.27.1",
48
50
  "chalk": "^5.4.1",
49
51
  "commander": "^13.1.0",
50
- "ora": "^8.2.0"
52
+ "ora": "^8.2.0",
53
+ "zod": "^4.3.6"
51
54
  },
52
55
  "devDependencies": {
53
56
  "@types/node": "^22.13.0",