Skip to content

Commit dfbf1cf

Browse files
authored
Update MCP to support new railway deployments list command from v4.10.0 of the CLI (#13)
# Why Claude can't do much with the logs command because it doesn't know how to find deployment IDs. This fixes that # What Changed - mv domain code to `src/cli/domain.ts` - add description to `src/tools/get-logs.ts` to help LLMs figure out they need deployment IDs - add `src/tools/list-deployments.ts` + corresponding CLI code to `src/cli/deployment.ts` # Test Plan - publish a project w/ a build error - vibe <img width="1255" height="489" alt="image" src="https://github.com/user-attachments/assets/f119f69e-f668-4cf7-bd7a-70fc9123e10a" /> <img width="1279" height="468" alt="image" src="https://github.com/user-attachments/assets/fba1d300-5ff7-4949-8833-35ebc5db592d" />
1 parent c9380d4 commit dfbf1cf

File tree

10 files changed

+508
-152
lines changed

10 files changed

+508
-152
lines changed

src/cli/deployment.test.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2+
import * as core from "./core";
3+
import { listDeployments } from "./deployment";
4+
import * as errorHandling from "./error-handling";
5+
import type { RailwayProject } from "./projects";
6+
import * as projects from "./projects";
7+
import * as version from "./version";
8+
9+
// Mock the dependencies
10+
vi.mock("./core");
11+
vi.mock("./error-handling");
12+
vi.mock("./projects");
13+
vi.mock("./version");
14+
15+
describe("listDeployments", () => {
16+
const mockWorkspacePath = "/test/workspace";
17+
18+
const mockProject: RailwayProject = {
19+
id: "test-project-id",
20+
name: "test-project",
21+
createdAt: "2024-01-01T00:00:00Z",
22+
updatedAt: "2024-01-01T00:00:00Z",
23+
};
24+
25+
beforeEach(() => {
26+
vi.clearAllMocks();
27+
28+
const mockOutput = "Deployment 1\nDeployment 2\nDeployment 3";
29+
vi.mocked(core.runRailwayCommand).mockResolvedValue({
30+
output: mockOutput,
31+
stderr: "",
32+
stdout: mockOutput,
33+
});
34+
vi.mocked(core.checkRailwayCliStatus).mockResolvedValue();
35+
vi.mocked(projects.getLinkedProjectInfo).mockResolvedValue({
36+
success: true,
37+
project: mockProject,
38+
});
39+
vi.mocked(version.getCliFeatureSupport).mockResolvedValue({
40+
logs: {
41+
args: {
42+
lines: true,
43+
filter: true,
44+
},
45+
},
46+
deployment: {
47+
list: true,
48+
},
49+
});
50+
vi.mocked(version.getRailwayVersion).mockResolvedValue("4.10.0");
51+
});
52+
53+
afterEach(() => {
54+
vi.restoreAllMocks();
55+
});
56+
57+
describe("successful cases", () => {
58+
it("should list deployments with default options", async () => {
59+
const result = await listDeployments({
60+
workspacePath: mockWorkspacePath,
61+
});
62+
63+
expect(result.success).toBe(true);
64+
expect(core.runRailwayCommand).toHaveBeenCalledWith(
65+
"railway deployment list --limit 20",
66+
mockWorkspacePath
67+
);
68+
});
69+
70+
it("should list deployments with JSON output", async () => {
71+
const result = await listDeployments({
72+
workspacePath: mockWorkspacePath,
73+
json: true,
74+
});
75+
76+
expect(result.success).toBe(true);
77+
expect(core.runRailwayCommand).toHaveBeenCalledWith(
78+
"railway deployment list --limit 20 --json",
79+
mockWorkspacePath
80+
);
81+
});
82+
83+
it("should list deployments with service filter", async () => {
84+
const result = await listDeployments({
85+
workspacePath: mockWorkspacePath,
86+
service: "my-api-service",
87+
});
88+
89+
expect(result.success).toBe(true);
90+
expect(core.runRailwayCommand).toHaveBeenCalledWith(
91+
"railway deployment list --service my-api-service --limit 20",
92+
mockWorkspacePath
93+
);
94+
});
95+
96+
it("should list deployments with environment filter", async () => {
97+
const mockOutput = "Environment deployment output";
98+
vi.mocked(core.runRailwayCommand).mockResolvedValue({
99+
output: mockOutput,
100+
stderr: "",
101+
stdout: mockOutput,
102+
});
103+
104+
const result = await listDeployments({
105+
workspacePath: mockWorkspacePath,
106+
environment: "staging",
107+
});
108+
109+
expect(result.success).toBe(true);
110+
expect(core.runRailwayCommand).toHaveBeenCalledWith(
111+
"railway deployment list --environment staging --limit 20",
112+
mockWorkspacePath
113+
);
114+
});
115+
116+
it("should list deployments with custom limit", async () => {
117+
const mockOutput = "Limited deployment output";
118+
vi.mocked(core.runRailwayCommand).mockResolvedValue({
119+
output: mockOutput,
120+
stderr: "",
121+
stdout: mockOutput,
122+
});
123+
124+
const result = await listDeployments({
125+
workspacePath: mockWorkspacePath,
126+
limit: 50,
127+
});
128+
129+
expect(result.success).toBe(true);
130+
expect(core.runRailwayCommand).toHaveBeenCalledWith(
131+
"railway deployment list --limit 50",
132+
mockWorkspacePath
133+
);
134+
});
135+
});
136+
137+
it("should return error when CLI version does not support deployment list", async () => {
138+
vi.mocked(version.getCliFeatureSupport).mockResolvedValue({
139+
logs: {
140+
args: {
141+
lines: false,
142+
filter: false,
143+
},
144+
},
145+
deployment: {
146+
list: false,
147+
},
148+
});
149+
vi.mocked(version.getRailwayVersion).mockResolvedValue("4.9.0");
150+
151+
const result = await listDeployments({
152+
workspacePath: mockWorkspacePath,
153+
});
154+
155+
expect(result.success).toBe(false);
156+
if (result.success) {
157+
throw new Error("Expected error");
158+
}
159+
expect(result.error).toBe(
160+
"Railway CLI version 4.9.0 does not support 'deployment list' command. Please upgrade to version 4.10.0 or later."
161+
);
162+
});
163+
});

0 commit comments

Comments
 (0)