@vucinatim/agentic-devtools 0.1.3 → 0.1.4
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 +15 -0
- package/docs/auth-and-setup-guidelines.md +13 -0
- package/docs/usage.md +17 -0
- package/package.json +1 -1
- package/src/tools/railway/client.mjs +736 -0
- package/src/tools/railway/mcp.mjs +532 -2
package/README.md
CHANGED
|
@@ -138,6 +138,21 @@ GitHub Actions Trusted Publishing over local write tokens. Use
|
|
|
138
138
|
`setup-publishing npm` to run npm's official Trusted Publishing setup command
|
|
139
139
|
through the package.
|
|
140
140
|
|
|
141
|
+
### Railway Coverage
|
|
142
|
+
|
|
143
|
+
The Railway tool now targets parity with Railway's documented public API for:
|
|
144
|
+
|
|
145
|
+
- projects and project members
|
|
146
|
+
- environments
|
|
147
|
+
- services and service instances
|
|
148
|
+
- deployments
|
|
149
|
+
- variables
|
|
150
|
+
- Railway-managed and custom domains
|
|
151
|
+
- volumes
|
|
152
|
+
|
|
153
|
+
This does not currently claim parity for undocumented dashboard-only concepts
|
|
154
|
+
such as canvas grouping internals.
|
|
155
|
+
|
|
141
156
|
Trusted Publishing notes:
|
|
142
157
|
|
|
143
158
|
- npm currently documents Trusted Publishing for GitHub-hosted GitHub Actions,
|
|
@@ -159,6 +159,19 @@ Validation guidance:
|
|
|
159
159
|
workspace-compatible reads
|
|
160
160
|
- keep identity-only queries such as `me` as separate optional capabilities
|
|
161
161
|
|
|
162
|
+
Current Railway management scope should cover the documented public API for:
|
|
163
|
+
|
|
164
|
+
- projects and project members
|
|
165
|
+
- environments
|
|
166
|
+
- services and service instances
|
|
167
|
+
- deployments
|
|
168
|
+
- variables
|
|
169
|
+
- service domains and custom domains
|
|
170
|
+
- volumes
|
|
171
|
+
|
|
172
|
+
Do not claim parity for dashboard-only or undocumented organization surfaces
|
|
173
|
+
until Railway documents them as stable public API.
|
|
174
|
+
|
|
162
175
|
## Namecheap
|
|
163
176
|
|
|
164
177
|
Namecheap is Tier 3.
|
package/docs/usage.md
CHANGED
|
@@ -157,6 +157,23 @@ Validation behavior:
|
|
|
157
157
|
- identity-style `me` queries are not treated as the compatibility baseline for
|
|
158
158
|
all bearer tokens
|
|
159
159
|
|
|
160
|
+
Railway capability coverage:
|
|
161
|
+
|
|
162
|
+
- read and mutate projects
|
|
163
|
+
- list project members
|
|
164
|
+
- read and mutate environments
|
|
165
|
+
- read and mutate services
|
|
166
|
+
- read and mutate service instance settings and limits
|
|
167
|
+
- deploy and redeploy service instances
|
|
168
|
+
- inspect deployments and list deployment history
|
|
169
|
+
- upsert and delete variables
|
|
170
|
+
- create, update, and delete Railway-managed and custom domains
|
|
171
|
+
- create and delete volumes
|
|
172
|
+
|
|
173
|
+
Not currently included as a supported public contract:
|
|
174
|
+
|
|
175
|
+
- undocumented dashboard canvas/group organization internals
|
|
176
|
+
|
|
160
177
|
Namecheap supports:
|
|
161
178
|
|
|
162
179
|
- guided local setup through `agentic-devtools connect namecheap`
|
package/package.json
CHANGED
|
@@ -86,6 +86,16 @@ export const createRailwayClient = ({
|
|
|
86
86
|
);
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
+
const requireResolvedProjectId = async (projectId, operation) => {
|
|
90
|
+
try {
|
|
91
|
+
return await resolveProjectId(projectId);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new RailwayApiError(
|
|
94
|
+
error instanceof Error ? error.message : `${operation} requires a Railway project id.`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
89
99
|
const getCurrentViewer = async () => {
|
|
90
100
|
requireAccountToken("getRailwayViewer");
|
|
91
101
|
const data = await request(`
|
|
@@ -231,6 +241,27 @@ export const createRailwayClient = ({
|
|
|
231
241
|
};
|
|
232
242
|
};
|
|
233
243
|
|
|
244
|
+
const getProjectMembers = async (projectId) => {
|
|
245
|
+
requireAccountToken("getRailwayProjectMembers");
|
|
246
|
+
const id = await requireResolvedProjectId(projectId, "getRailwayProjectMembers");
|
|
247
|
+
const data = await request(
|
|
248
|
+
`
|
|
249
|
+
query RailwayProjectMembers($projectId: String!) {
|
|
250
|
+
projectMembers(projectId: $projectId) {
|
|
251
|
+
id
|
|
252
|
+
role
|
|
253
|
+
user {
|
|
254
|
+
name
|
|
255
|
+
email
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
`,
|
|
260
|
+
{ projectId: id },
|
|
261
|
+
);
|
|
262
|
+
return data.projectMembers;
|
|
263
|
+
};
|
|
264
|
+
|
|
234
265
|
const listEnvironments = async ({ projectId, isEphemeral } = {}) => {
|
|
235
266
|
const resolvedProjectId = await resolveProjectId(projectId);
|
|
236
267
|
const data = await request(
|
|
@@ -308,6 +339,675 @@ export const createRailwayClient = ({
|
|
|
308
339
|
};
|
|
309
340
|
};
|
|
310
341
|
|
|
342
|
+
const getService = async (serviceId) => {
|
|
343
|
+
const data = await request(
|
|
344
|
+
`
|
|
345
|
+
query RailwayService($id: String!) {
|
|
346
|
+
service(id: $id) {
|
|
347
|
+
id
|
|
348
|
+
name
|
|
349
|
+
icon
|
|
350
|
+
createdAt
|
|
351
|
+
updatedAt
|
|
352
|
+
deletedAt
|
|
353
|
+
featureFlags
|
|
354
|
+
project {
|
|
355
|
+
id
|
|
356
|
+
name
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
`,
|
|
361
|
+
{ id: serviceId },
|
|
362
|
+
);
|
|
363
|
+
return {
|
|
364
|
+
...data.service,
|
|
365
|
+
projectId: data.service.project?.id ?? null,
|
|
366
|
+
projectName: data.service.project?.name ?? null,
|
|
367
|
+
};
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const getServiceInstance = async ({ serviceId, environmentId }) => {
|
|
371
|
+
const data = await request(
|
|
372
|
+
`
|
|
373
|
+
query RailwayServiceInstance($serviceId: String!, $environmentId: String!) {
|
|
374
|
+
serviceInstance(serviceId: $serviceId, environmentId: $environmentId) {
|
|
375
|
+
id
|
|
376
|
+
serviceId
|
|
377
|
+
serviceName
|
|
378
|
+
environmentId
|
|
379
|
+
rootDirectory
|
|
380
|
+
railwayConfigFile
|
|
381
|
+
buildCommand
|
|
382
|
+
startCommand
|
|
383
|
+
healthcheckPath
|
|
384
|
+
cronSchedule
|
|
385
|
+
latestDeployment {
|
|
386
|
+
id
|
|
387
|
+
status
|
|
388
|
+
url
|
|
389
|
+
staticUrl
|
|
390
|
+
}
|
|
391
|
+
domains {
|
|
392
|
+
serviceDomains {
|
|
393
|
+
id
|
|
394
|
+
domain
|
|
395
|
+
}
|
|
396
|
+
customDomains {
|
|
397
|
+
id
|
|
398
|
+
domain
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
`,
|
|
404
|
+
{ serviceId, environmentId },
|
|
405
|
+
);
|
|
406
|
+
return data.serviceInstance;
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const getServiceInstanceLimits = async ({ serviceId, environmentId }) => {
|
|
410
|
+
const data = await request(
|
|
411
|
+
`
|
|
412
|
+
query RailwayServiceInstanceLimits($serviceId: String!, $environmentId: String!) {
|
|
413
|
+
serviceInstanceLimits(serviceId: $serviceId, environmentId: $environmentId)
|
|
414
|
+
}
|
|
415
|
+
`,
|
|
416
|
+
{ serviceId, environmentId },
|
|
417
|
+
);
|
|
418
|
+
return data.serviceInstanceLimits;
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const getDeployment = async (deploymentId) => {
|
|
422
|
+
const data = await request(
|
|
423
|
+
`
|
|
424
|
+
query RailwayDeployment($id: String!) {
|
|
425
|
+
deployment(id: $id) {
|
|
426
|
+
id
|
|
427
|
+
status
|
|
428
|
+
createdAt
|
|
429
|
+
updatedAt
|
|
430
|
+
statusUpdatedAt
|
|
431
|
+
canRedeploy
|
|
432
|
+
canRollback
|
|
433
|
+
deploymentStopped
|
|
434
|
+
environmentId
|
|
435
|
+
projectId
|
|
436
|
+
serviceId
|
|
437
|
+
url
|
|
438
|
+
staticUrl
|
|
439
|
+
service {
|
|
440
|
+
id
|
|
441
|
+
name
|
|
442
|
+
}
|
|
443
|
+
environment {
|
|
444
|
+
id
|
|
445
|
+
name
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
`,
|
|
450
|
+
{ id: deploymentId },
|
|
451
|
+
);
|
|
452
|
+
return {
|
|
453
|
+
...data.deployment,
|
|
454
|
+
serviceName: data.deployment.service?.name ?? null,
|
|
455
|
+
environmentName: data.deployment.environment?.name ?? null,
|
|
456
|
+
};
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const listDeployments = async ({
|
|
460
|
+
projectId,
|
|
461
|
+
environmentId,
|
|
462
|
+
serviceId,
|
|
463
|
+
first = 20,
|
|
464
|
+
after = null,
|
|
465
|
+
before = null,
|
|
466
|
+
last = null,
|
|
467
|
+
} = {}) => {
|
|
468
|
+
const resolvedProjectId =
|
|
469
|
+
projectId == null && auth.kind === "project"
|
|
470
|
+
? await requireResolvedProjectId(null, "listRailwayDeployments")
|
|
471
|
+
: projectId;
|
|
472
|
+
|
|
473
|
+
const input = compactObject({
|
|
474
|
+
projectId: resolvedProjectId,
|
|
475
|
+
environmentId,
|
|
476
|
+
serviceId,
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const data = await request(
|
|
480
|
+
`
|
|
481
|
+
query RailwayDeployments(
|
|
482
|
+
$input: DeploymentListInput!
|
|
483
|
+
$first: Int
|
|
484
|
+
$after: String
|
|
485
|
+
$before: String
|
|
486
|
+
$last: Int
|
|
487
|
+
) {
|
|
488
|
+
deployments(
|
|
489
|
+
input: $input
|
|
490
|
+
first: $first
|
|
491
|
+
after: $after
|
|
492
|
+
before: $before
|
|
493
|
+
last: $last
|
|
494
|
+
) {
|
|
495
|
+
edges {
|
|
496
|
+
node {
|
|
497
|
+
id
|
|
498
|
+
status
|
|
499
|
+
createdAt
|
|
500
|
+
updatedAt
|
|
501
|
+
environmentId
|
|
502
|
+
projectId
|
|
503
|
+
serviceId
|
|
504
|
+
url
|
|
505
|
+
staticUrl
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
pageInfo {
|
|
509
|
+
hasNextPage
|
|
510
|
+
hasPreviousPage
|
|
511
|
+
startCursor
|
|
512
|
+
endCursor
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
`,
|
|
517
|
+
{ input, first, after, before, last },
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
return {
|
|
521
|
+
deployments: connectionNodes(data.deployments),
|
|
522
|
+
pageInfo: data.deployments?.pageInfo ?? null,
|
|
523
|
+
};
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
const createProject = async (input = {}) => {
|
|
527
|
+
requireAccountToken("createRailwayProject");
|
|
528
|
+
const data = await request(
|
|
529
|
+
`
|
|
530
|
+
mutation RailwayProjectCreate($input: ProjectCreateInput!) {
|
|
531
|
+
projectCreate(input: $input) {
|
|
532
|
+
id
|
|
533
|
+
name
|
|
534
|
+
description
|
|
535
|
+
workspace {
|
|
536
|
+
id
|
|
537
|
+
name
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
`,
|
|
542
|
+
{ input: compactObject(input) },
|
|
543
|
+
);
|
|
544
|
+
return data.projectCreate;
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
const updateProject = async ({ projectId, ...input } = {}) => {
|
|
548
|
+
requireAccountToken("updateRailwayProject");
|
|
549
|
+
const id = await requireResolvedProjectId(projectId, "updateRailwayProject");
|
|
550
|
+
const data = await request(
|
|
551
|
+
`
|
|
552
|
+
mutation RailwayProjectUpdate($id: String!, $input: ProjectUpdateInput!) {
|
|
553
|
+
projectUpdate(id: $id, input: $input) {
|
|
554
|
+
id
|
|
555
|
+
name
|
|
556
|
+
description
|
|
557
|
+
prDeploys
|
|
558
|
+
focusedPrEnvironments
|
|
559
|
+
botPrEnvironments
|
|
560
|
+
isPublic
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
`,
|
|
564
|
+
{ id, input: compactObject(input) },
|
|
565
|
+
);
|
|
566
|
+
return data.projectUpdate;
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const deleteProject = async (projectId) => {
|
|
570
|
+
requireAccountToken("deleteRailwayProject");
|
|
571
|
+
const id = await requireResolvedProjectId(projectId, "deleteRailwayProject");
|
|
572
|
+
const deleted = await request(
|
|
573
|
+
`
|
|
574
|
+
mutation RailwayProjectDelete($id: String!) {
|
|
575
|
+
projectDelete(id: $id)
|
|
576
|
+
}
|
|
577
|
+
`,
|
|
578
|
+
{ id },
|
|
579
|
+
);
|
|
580
|
+
return {
|
|
581
|
+
deleted: Boolean(deleted.projectDelete),
|
|
582
|
+
projectId: id,
|
|
583
|
+
};
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const transferProject = async ({ projectId, workspaceId } = {}) => {
|
|
587
|
+
requireAccountToken("transferRailwayProject");
|
|
588
|
+
const id = await requireResolvedProjectId(projectId, "transferRailwayProject");
|
|
589
|
+
const transferred = await request(
|
|
590
|
+
`
|
|
591
|
+
mutation RailwayProjectTransfer($projectId: String!, $input: ProjectTransferInput!) {
|
|
592
|
+
projectTransfer(projectId: $projectId, input: $input)
|
|
593
|
+
}
|
|
594
|
+
`,
|
|
595
|
+
{
|
|
596
|
+
projectId: id,
|
|
597
|
+
input: { workspaceId },
|
|
598
|
+
},
|
|
599
|
+
);
|
|
600
|
+
return {
|
|
601
|
+
transferred: Boolean(transferred.projectTransfer),
|
|
602
|
+
projectId: id,
|
|
603
|
+
workspaceId,
|
|
604
|
+
};
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
const createService = async (input = {}) => {
|
|
608
|
+
const data = await request(
|
|
609
|
+
`
|
|
610
|
+
mutation RailwayServiceCreate($input: ServiceCreateInput!) {
|
|
611
|
+
serviceCreate(input: $input) {
|
|
612
|
+
id
|
|
613
|
+
name
|
|
614
|
+
icon
|
|
615
|
+
projectId
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
`,
|
|
619
|
+
{ input: compactObject(input) },
|
|
620
|
+
);
|
|
621
|
+
return data.serviceCreate;
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
const updateService = async ({ serviceId, ...input } = {}) => {
|
|
625
|
+
const data = await request(
|
|
626
|
+
`
|
|
627
|
+
mutation RailwayServiceUpdate($id: String!, $input: ServiceUpdateInput!) {
|
|
628
|
+
serviceUpdate(id: $id, input: $input) {
|
|
629
|
+
id
|
|
630
|
+
name
|
|
631
|
+
icon
|
|
632
|
+
projectId
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
`,
|
|
636
|
+
{ id: serviceId, input: compactObject(input) },
|
|
637
|
+
);
|
|
638
|
+
return data.serviceUpdate;
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
const connectService = async ({ serviceId, ...input } = {}) => {
|
|
642
|
+
const data = await request(
|
|
643
|
+
`
|
|
644
|
+
mutation RailwayServiceConnect($id: String!, $input: ServiceConnectInput!) {
|
|
645
|
+
serviceConnect(id: $id, input: $input) {
|
|
646
|
+
id
|
|
647
|
+
name
|
|
648
|
+
icon
|
|
649
|
+
projectId
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
`,
|
|
653
|
+
{ id: serviceId, input: compactObject(input) },
|
|
654
|
+
);
|
|
655
|
+
return data.serviceConnect;
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
const disconnectService = async (serviceId) => {
|
|
659
|
+
const disconnected = await request(
|
|
660
|
+
`
|
|
661
|
+
mutation RailwayServiceDisconnect($id: String!) {
|
|
662
|
+
serviceDisconnect(id: $id)
|
|
663
|
+
}
|
|
664
|
+
`,
|
|
665
|
+
{ id: serviceId },
|
|
666
|
+
);
|
|
667
|
+
return {
|
|
668
|
+
disconnected: Boolean(disconnected.serviceDisconnect),
|
|
669
|
+
serviceId,
|
|
670
|
+
};
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
const deleteService = async ({ serviceId, environmentId } = {}) => {
|
|
674
|
+
const deleted = await request(
|
|
675
|
+
`
|
|
676
|
+
mutation RailwayServiceDelete($id: String!, $environmentId: String) {
|
|
677
|
+
serviceDelete(id: $id, environmentId: $environmentId)
|
|
678
|
+
}
|
|
679
|
+
`,
|
|
680
|
+
{ id: serviceId, environmentId },
|
|
681
|
+
);
|
|
682
|
+
return {
|
|
683
|
+
deleted: Boolean(deleted.serviceDelete),
|
|
684
|
+
serviceId,
|
|
685
|
+
environmentId: environmentId ?? null,
|
|
686
|
+
};
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
const updateServiceInstance = async ({
|
|
690
|
+
serviceId,
|
|
691
|
+
environmentId,
|
|
692
|
+
...input
|
|
693
|
+
} = {}) => {
|
|
694
|
+
const updated = await request(
|
|
695
|
+
`
|
|
696
|
+
mutation RailwayServiceInstanceUpdate(
|
|
697
|
+
$serviceId: String!
|
|
698
|
+
$environmentId: String
|
|
699
|
+
$input: ServiceInstanceUpdateInput!
|
|
700
|
+
) {
|
|
701
|
+
serviceInstanceUpdate(
|
|
702
|
+
serviceId: $serviceId
|
|
703
|
+
environmentId: $environmentId
|
|
704
|
+
input: $input
|
|
705
|
+
)
|
|
706
|
+
}
|
|
707
|
+
`,
|
|
708
|
+
{
|
|
709
|
+
serviceId,
|
|
710
|
+
environmentId,
|
|
711
|
+
input: compactObject(input),
|
|
712
|
+
},
|
|
713
|
+
);
|
|
714
|
+
return {
|
|
715
|
+
updated: Boolean(updated.serviceInstanceUpdate),
|
|
716
|
+
serviceId,
|
|
717
|
+
environmentId: environmentId ?? null,
|
|
718
|
+
};
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
const deployService = async ({
|
|
722
|
+
serviceId,
|
|
723
|
+
environmentId,
|
|
724
|
+
commitSha,
|
|
725
|
+
latestCommit,
|
|
726
|
+
} = {}) => {
|
|
727
|
+
const deployment = await request(
|
|
728
|
+
`
|
|
729
|
+
mutation RailwayServiceInstanceDeploy(
|
|
730
|
+
$serviceId: String!
|
|
731
|
+
$environmentId: String!
|
|
732
|
+
$commitSha: String
|
|
733
|
+
$latestCommit: Boolean
|
|
734
|
+
) {
|
|
735
|
+
serviceInstanceDeploy(
|
|
736
|
+
serviceId: $serviceId
|
|
737
|
+
environmentId: $environmentId
|
|
738
|
+
commitSha: $commitSha
|
|
739
|
+
latestCommit: $latestCommit
|
|
740
|
+
) {
|
|
741
|
+
id
|
|
742
|
+
status
|
|
743
|
+
environmentId
|
|
744
|
+
serviceId
|
|
745
|
+
url
|
|
746
|
+
staticUrl
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
`,
|
|
750
|
+
{ serviceId, environmentId, commitSha, latestCommit },
|
|
751
|
+
);
|
|
752
|
+
return deployment.serviceInstanceDeploy;
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const redeployService = async ({ serviceId, environmentId } = {}) => {
|
|
756
|
+
const deployment = await request(
|
|
757
|
+
`
|
|
758
|
+
mutation RailwayServiceInstanceRedeploy(
|
|
759
|
+
$serviceId: String!
|
|
760
|
+
$environmentId: String!
|
|
761
|
+
) {
|
|
762
|
+
serviceInstanceRedeploy(
|
|
763
|
+
serviceId: $serviceId
|
|
764
|
+
environmentId: $environmentId
|
|
765
|
+
) {
|
|
766
|
+
id
|
|
767
|
+
status
|
|
768
|
+
environmentId
|
|
769
|
+
serviceId
|
|
770
|
+
url
|
|
771
|
+
staticUrl
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
`,
|
|
775
|
+
{ serviceId, environmentId },
|
|
776
|
+
);
|
|
777
|
+
return deployment.serviceInstanceRedeploy;
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
const updateServiceInstanceLimits = async ({
|
|
781
|
+
serviceId,
|
|
782
|
+
environmentId,
|
|
783
|
+
memoryGB,
|
|
784
|
+
vCPUs,
|
|
785
|
+
} = {}) => {
|
|
786
|
+
const updated = await request(
|
|
787
|
+
`
|
|
788
|
+
mutation RailwayServiceInstanceLimitsUpdate(
|
|
789
|
+
$input: ServiceInstanceLimitsUpdateInput!
|
|
790
|
+
) {
|
|
791
|
+
serviceInstanceLimitsUpdate(input: $input)
|
|
792
|
+
}
|
|
793
|
+
`,
|
|
794
|
+
{
|
|
795
|
+
input: compactObject({
|
|
796
|
+
serviceId,
|
|
797
|
+
environmentId,
|
|
798
|
+
memoryGB,
|
|
799
|
+
vCPUs,
|
|
800
|
+
}),
|
|
801
|
+
},
|
|
802
|
+
);
|
|
803
|
+
return {
|
|
804
|
+
updated: Boolean(updated.serviceInstanceLimitsUpdate),
|
|
805
|
+
serviceId,
|
|
806
|
+
environmentId,
|
|
807
|
+
memoryGB: memoryGB ?? null,
|
|
808
|
+
vCPUs: vCPUs ?? null,
|
|
809
|
+
};
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
const createEnvironment = async (input = {}) => {
|
|
813
|
+
const environment = await request(
|
|
814
|
+
`
|
|
815
|
+
mutation RailwayEnvironmentCreate($input: EnvironmentCreateInput!) {
|
|
816
|
+
environmentCreate(input: $input) {
|
|
817
|
+
id
|
|
818
|
+
name
|
|
819
|
+
isEphemeral
|
|
820
|
+
projectId
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
`,
|
|
824
|
+
{ input: compactObject(input) },
|
|
825
|
+
);
|
|
826
|
+
return environment.environmentCreate;
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
const deleteEnvironment = async (environmentId) => {
|
|
830
|
+
const deleted = await request(
|
|
831
|
+
`
|
|
832
|
+
mutation RailwayEnvironmentDelete($id: String!) {
|
|
833
|
+
environmentDelete(id: $id)
|
|
834
|
+
}
|
|
835
|
+
`,
|
|
836
|
+
{ id: environmentId },
|
|
837
|
+
);
|
|
838
|
+
return {
|
|
839
|
+
deleted: Boolean(deleted.environmentDelete),
|
|
840
|
+
environmentId,
|
|
841
|
+
};
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
const upsertVariable = async (input = {}) => {
|
|
845
|
+
const updated = await request(
|
|
846
|
+
`
|
|
847
|
+
mutation RailwayVariableUpsert($input: VariableUpsertInput!) {
|
|
848
|
+
variableUpsert(input: $input)
|
|
849
|
+
}
|
|
850
|
+
`,
|
|
851
|
+
{ input: compactObject(input) },
|
|
852
|
+
);
|
|
853
|
+
return {
|
|
854
|
+
updated: Boolean(updated.variableUpsert),
|
|
855
|
+
name: input.name ?? null,
|
|
856
|
+
environmentId: input.environmentId ?? null,
|
|
857
|
+
serviceId: input.serviceId ?? null,
|
|
858
|
+
projectId: input.projectId ?? null,
|
|
859
|
+
};
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
const deleteVariable = async (input = {}) => {
|
|
863
|
+
const deleted = await request(
|
|
864
|
+
`
|
|
865
|
+
mutation RailwayVariableDelete($input: VariableDeleteInput!) {
|
|
866
|
+
variableDelete(input: $input)
|
|
867
|
+
}
|
|
868
|
+
`,
|
|
869
|
+
{ input: compactObject(input) },
|
|
870
|
+
);
|
|
871
|
+
return {
|
|
872
|
+
deleted: Boolean(deleted.variableDelete),
|
|
873
|
+
name: input.name ?? null,
|
|
874
|
+
environmentId: input.environmentId ?? null,
|
|
875
|
+
serviceId: input.serviceId ?? null,
|
|
876
|
+
projectId: input.projectId ?? null,
|
|
877
|
+
};
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
const createServiceDomain = async (input = {}) => {
|
|
881
|
+
const domain = await request(
|
|
882
|
+
`
|
|
883
|
+
mutation RailwayServiceDomainCreate($input: ServiceDomainCreateInput!) {
|
|
884
|
+
serviceDomainCreate(input: $input) {
|
|
885
|
+
id
|
|
886
|
+
domain
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
`,
|
|
890
|
+
{ input: compactObject(input) },
|
|
891
|
+
);
|
|
892
|
+
return domain.serviceDomainCreate;
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
const updateServiceDomain = async (input = {}) => {
|
|
896
|
+
const updated = await request(
|
|
897
|
+
`
|
|
898
|
+
mutation RailwayServiceDomainUpdate($input: ServiceDomainUpdateInput!) {
|
|
899
|
+
serviceDomainUpdate(input: $input)
|
|
900
|
+
}
|
|
901
|
+
`,
|
|
902
|
+
{ input: compactObject(input) },
|
|
903
|
+
);
|
|
904
|
+
return {
|
|
905
|
+
updated: Boolean(updated.serviceDomainUpdate),
|
|
906
|
+
serviceDomainId: input.serviceDomainId ?? null,
|
|
907
|
+
};
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
const deleteServiceDomain = async (serviceDomainId) => {
|
|
911
|
+
const deleted = await request(
|
|
912
|
+
`
|
|
913
|
+
mutation RailwayServiceDomainDelete($id: String!) {
|
|
914
|
+
serviceDomainDelete(id: $id)
|
|
915
|
+
}
|
|
916
|
+
`,
|
|
917
|
+
{ id: serviceDomainId },
|
|
918
|
+
);
|
|
919
|
+
return {
|
|
920
|
+
deleted: Boolean(deleted.serviceDomainDelete),
|
|
921
|
+
serviceDomainId,
|
|
922
|
+
};
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
const createCustomDomain = async (input = {}) => {
|
|
926
|
+
const domain = await request(
|
|
927
|
+
`
|
|
928
|
+
mutation RailwayCustomDomainCreate($input: CustomDomainCreateInput!) {
|
|
929
|
+
customDomainCreate(input: $input) {
|
|
930
|
+
id
|
|
931
|
+
domain
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
`,
|
|
935
|
+
{ input: compactObject(input) },
|
|
936
|
+
);
|
|
937
|
+
return domain.customDomainCreate;
|
|
938
|
+
};
|
|
939
|
+
|
|
940
|
+
const updateCustomDomain = async ({
|
|
941
|
+
customDomainId,
|
|
942
|
+
environmentId,
|
|
943
|
+
targetPort,
|
|
944
|
+
} = {}) => {
|
|
945
|
+
const updated = await request(
|
|
946
|
+
`
|
|
947
|
+
mutation RailwayCustomDomainUpdate(
|
|
948
|
+
$id: String!
|
|
949
|
+
$environmentId: String!
|
|
950
|
+
$targetPort: Int
|
|
951
|
+
) {
|
|
952
|
+
customDomainUpdate(
|
|
953
|
+
id: $id
|
|
954
|
+
environmentId: $environmentId
|
|
955
|
+
targetPort: $targetPort
|
|
956
|
+
)
|
|
957
|
+
}
|
|
958
|
+
`,
|
|
959
|
+
{ id: customDomainId, environmentId, targetPort },
|
|
960
|
+
);
|
|
961
|
+
return {
|
|
962
|
+
updated: Boolean(updated.customDomainUpdate),
|
|
963
|
+
customDomainId,
|
|
964
|
+
};
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
const deleteCustomDomain = async (customDomainId) => {
|
|
968
|
+
const deleted = await request(
|
|
969
|
+
`
|
|
970
|
+
mutation RailwayCustomDomainDelete($id: String!) {
|
|
971
|
+
customDomainDelete(id: $id)
|
|
972
|
+
}
|
|
973
|
+
`,
|
|
974
|
+
{ id: customDomainId },
|
|
975
|
+
);
|
|
976
|
+
return {
|
|
977
|
+
deleted: Boolean(deleted.customDomainDelete),
|
|
978
|
+
customDomainId,
|
|
979
|
+
};
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
const createVolume = async (input = {}) => {
|
|
983
|
+
const volume = await request(
|
|
984
|
+
`
|
|
985
|
+
mutation RailwayVolumeCreate($input: VolumeCreateInput!) {
|
|
986
|
+
volumeCreate(input: $input) {
|
|
987
|
+
id
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
`,
|
|
991
|
+
{ input: compactObject(input) },
|
|
992
|
+
);
|
|
993
|
+
return volume.volumeCreate;
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
const deleteVolume = async (volumeId) => {
|
|
997
|
+
const deleted = await request(
|
|
998
|
+
`
|
|
999
|
+
mutation RailwayVolumeDelete($volumeId: String!) {
|
|
1000
|
+
volumeDelete(volumeId: $volumeId)
|
|
1001
|
+
}
|
|
1002
|
+
`,
|
|
1003
|
+
{ volumeId },
|
|
1004
|
+
);
|
|
1005
|
+
return {
|
|
1006
|
+
deleted: Boolean(deleted.volumeDelete),
|
|
1007
|
+
volumeId,
|
|
1008
|
+
};
|
|
1009
|
+
};
|
|
1010
|
+
|
|
311
1011
|
const doctorProject = async ({ projectId } = {}) => {
|
|
312
1012
|
const project = await getProject(projectId);
|
|
313
1013
|
const primaryEnvironmentId =
|
|
@@ -364,10 +1064,41 @@ export const createRailwayClient = ({
|
|
|
364
1064
|
getCurrentViewer,
|
|
365
1065
|
validateAccountToken,
|
|
366
1066
|
listProjects,
|
|
1067
|
+
createProject,
|
|
1068
|
+
updateProject,
|
|
1069
|
+
deleteProject,
|
|
1070
|
+
transferProject,
|
|
1071
|
+
getProjectMembers,
|
|
367
1072
|
getProjectTokenContext,
|
|
368
1073
|
getProject,
|
|
369
1074
|
listEnvironments,
|
|
370
1075
|
getEnvironment,
|
|
1076
|
+
createEnvironment,
|
|
1077
|
+
deleteEnvironment,
|
|
1078
|
+
getService,
|
|
1079
|
+
getServiceInstance,
|
|
1080
|
+
getServiceInstanceLimits,
|
|
1081
|
+
createService,
|
|
1082
|
+
updateService,
|
|
1083
|
+
connectService,
|
|
1084
|
+
disconnectService,
|
|
1085
|
+
deleteService,
|
|
1086
|
+
updateServiceInstance,
|
|
1087
|
+
deployService,
|
|
1088
|
+
redeployService,
|
|
1089
|
+
updateServiceInstanceLimits,
|
|
1090
|
+
getDeployment,
|
|
1091
|
+
listDeployments,
|
|
1092
|
+
upsertVariable,
|
|
1093
|
+
deleteVariable,
|
|
1094
|
+
createServiceDomain,
|
|
1095
|
+
updateServiceDomain,
|
|
1096
|
+
deleteServiceDomain,
|
|
1097
|
+
createCustomDomain,
|
|
1098
|
+
updateCustomDomain,
|
|
1099
|
+
deleteCustomDomain,
|
|
1100
|
+
createVolume,
|
|
1101
|
+
deleteVolume,
|
|
371
1102
|
doctorProject,
|
|
372
1103
|
};
|
|
373
1104
|
};
|
|
@@ -382,6 +1113,11 @@ const connectionNodes = (connection) => {
|
|
|
382
1113
|
return nodes;
|
|
383
1114
|
};
|
|
384
1115
|
|
|
1116
|
+
const compactObject = (value) =>
|
|
1117
|
+
Object.fromEntries(
|
|
1118
|
+
Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined),
|
|
1119
|
+
);
|
|
1120
|
+
|
|
385
1121
|
const hasErrors = (payload) =>
|
|
386
1122
|
typeof payload === "object" &&
|
|
387
1123
|
payload !== null &&
|
|
@@ -24,7 +24,8 @@ Optional environment variables:
|
|
|
24
24
|
RAILWAY_PROJECT_ID
|
|
25
25
|
RAILWAY_API_ENDPOINT
|
|
26
26
|
|
|
27
|
-
Project tokens can
|
|
27
|
+
Project tokens can manage resources scoped to the attached project environment.
|
|
28
|
+
Account and workspace tokens can inspect and manage broader Railway resources.
|
|
28
29
|
|
|
29
30
|
If env vars are not provided, use the connectRailway tool to open the browser-based setup flow and save a local Railway token.
|
|
30
31
|
`;
|
|
@@ -47,7 +48,7 @@ const createServer = () => {
|
|
|
47
48
|
},
|
|
48
49
|
{
|
|
49
50
|
instructions:
|
|
50
|
-
"Use these tools
|
|
51
|
+
"Use these tools to inspect and manage Railway projects, environments, services, domains, variables, deployments, and volumes through the documented public API. Prefer read tools first, then use targeted write tools. Delete tools are destructive and should be used deliberately.",
|
|
51
52
|
},
|
|
52
53
|
);
|
|
53
54
|
|
|
@@ -156,6 +157,21 @@ const createServer = () => {
|
|
|
156
157
|
),
|
|
157
158
|
);
|
|
158
159
|
|
|
160
|
+
server.registerTool(
|
|
161
|
+
"listRailwayProjectMembers",
|
|
162
|
+
{
|
|
163
|
+
description:
|
|
164
|
+
"List members of a Railway project. Requires an account or workspace token.",
|
|
165
|
+
inputSchema: {
|
|
166
|
+
projectId: z.string().min(1).optional(),
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
async ({ projectId } = {}) =>
|
|
170
|
+
createToolResult(
|
|
171
|
+
await withClient((client) => client.getProjectMembers(projectId)),
|
|
172
|
+
),
|
|
173
|
+
);
|
|
174
|
+
|
|
159
175
|
server.registerTool(
|
|
160
176
|
"inspectRailwayProjectToken",
|
|
161
177
|
{
|
|
@@ -183,6 +199,74 @@ const createServer = () => {
|
|
|
183
199
|
),
|
|
184
200
|
);
|
|
185
201
|
|
|
202
|
+
server.registerTool(
|
|
203
|
+
"createRailwayProject",
|
|
204
|
+
{
|
|
205
|
+
description:
|
|
206
|
+
"Create a Railway project. Requires an account or workspace token.",
|
|
207
|
+
inputSchema: {
|
|
208
|
+
name: z.string().min(1).optional(),
|
|
209
|
+
description: z.string().optional(),
|
|
210
|
+
workspaceId: z.string().min(1).optional(),
|
|
211
|
+
defaultEnvironmentName: z.string().min(1).optional(),
|
|
212
|
+
isMonorepo: z.boolean().optional(),
|
|
213
|
+
isPublic: z.boolean().optional(),
|
|
214
|
+
prDeploys: z.boolean().optional(),
|
|
215
|
+
runtime: z.string().min(1).optional(),
|
|
216
|
+
repo: z.unknown().optional(),
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
async (args = {}) =>
|
|
220
|
+
createToolResult(await withClient((client) => client.createProject(args))),
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
server.registerTool(
|
|
224
|
+
"updateRailwayProject",
|
|
225
|
+
{
|
|
226
|
+
description:
|
|
227
|
+
"Update Railway project settings such as name, description, or PR environment behavior.",
|
|
228
|
+
inputSchema: {
|
|
229
|
+
projectId: z.string().min(1).optional(),
|
|
230
|
+
name: z.string().min(1).optional(),
|
|
231
|
+
description: z.string().optional(),
|
|
232
|
+
baseEnvironmentId: z.string().min(1).optional(),
|
|
233
|
+
botPrEnvironments: z.boolean().optional(),
|
|
234
|
+
focusedPrEnvironments: z.boolean().optional(),
|
|
235
|
+
isPublic: z.boolean().optional(),
|
|
236
|
+
prDeploys: z.boolean().optional(),
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
async (args = {}) =>
|
|
240
|
+
createToolResult(await withClient((client) => client.updateProject(args))),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
server.registerTool(
|
|
244
|
+
"deleteRailwayProject",
|
|
245
|
+
{
|
|
246
|
+
description:
|
|
247
|
+
"Delete a Railway project. Destructive. Requires an account or workspace token.",
|
|
248
|
+
inputSchema: {
|
|
249
|
+
projectId: z.string().min(1).optional(),
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
async ({ projectId } = {}) =>
|
|
253
|
+
createToolResult(await withClient((client) => client.deleteProject(projectId))),
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
server.registerTool(
|
|
257
|
+
"transferRailwayProject",
|
|
258
|
+
{
|
|
259
|
+
description:
|
|
260
|
+
"Transfer a Railway project to another workspace. Requires an account or workspace token.",
|
|
261
|
+
inputSchema: {
|
|
262
|
+
projectId: z.string().min(1).optional(),
|
|
263
|
+
workspaceId: z.string().min(1),
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
async (args) =>
|
|
267
|
+
createToolResult(await withClient((client) => client.transferProject(args))),
|
|
268
|
+
);
|
|
269
|
+
|
|
186
270
|
server.registerTool(
|
|
187
271
|
"listRailwayEnvironments",
|
|
188
272
|
{
|
|
@@ -214,6 +298,452 @@ const createServer = () => {
|
|
|
214
298
|
),
|
|
215
299
|
);
|
|
216
300
|
|
|
301
|
+
server.registerTool(
|
|
302
|
+
"createRailwayEnvironment",
|
|
303
|
+
{
|
|
304
|
+
description:
|
|
305
|
+
"Create a Railway environment inside a project.",
|
|
306
|
+
inputSchema: {
|
|
307
|
+
projectId: z.string().min(1),
|
|
308
|
+
name: z.string().min(1),
|
|
309
|
+
ephemeral: z.boolean().optional(),
|
|
310
|
+
sourceEnvironmentId: z.string().min(1).optional(),
|
|
311
|
+
skipInitialDeploys: z.boolean().optional(),
|
|
312
|
+
stageInitialChanges: z.boolean().optional(),
|
|
313
|
+
applyChangesInBackground: z.boolean().optional(),
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
async (args) =>
|
|
317
|
+
createToolResult(await withClient((client) => client.createEnvironment(args))),
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
server.registerTool(
|
|
321
|
+
"deleteRailwayEnvironment",
|
|
322
|
+
{
|
|
323
|
+
description:
|
|
324
|
+
"Delete a Railway environment. Destructive.",
|
|
325
|
+
inputSchema: {
|
|
326
|
+
environmentId: z.string().min(1),
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
async ({ environmentId }) =>
|
|
330
|
+
createToolResult(
|
|
331
|
+
await withClient((client) => client.deleteEnvironment(environmentId)),
|
|
332
|
+
),
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
server.registerTool(
|
|
336
|
+
"getRailwayService",
|
|
337
|
+
{
|
|
338
|
+
description: "Inspect one Railway service.",
|
|
339
|
+
inputSchema: {
|
|
340
|
+
serviceId: z.string().min(1),
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
async ({ serviceId }) =>
|
|
344
|
+
createToolResult(await withClient((client) => client.getService(serviceId))),
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
server.registerTool(
|
|
348
|
+
"getRailwayServiceInstance",
|
|
349
|
+
{
|
|
350
|
+
description:
|
|
351
|
+
"Inspect one Railway service instance in a specific environment.",
|
|
352
|
+
inputSchema: {
|
|
353
|
+
serviceId: z.string().min(1),
|
|
354
|
+
environmentId: z.string().min(1),
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
async (args) =>
|
|
358
|
+
createToolResult(
|
|
359
|
+
await withClient((client) => client.getServiceInstance(args)),
|
|
360
|
+
),
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
server.registerTool(
|
|
364
|
+
"getRailwayServiceInstanceLimits",
|
|
365
|
+
{
|
|
366
|
+
description:
|
|
367
|
+
"Get resource limits for a Railway service instance.",
|
|
368
|
+
inputSchema: {
|
|
369
|
+
serviceId: z.string().min(1),
|
|
370
|
+
environmentId: z.string().min(1),
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
async (args) =>
|
|
374
|
+
createToolResult(
|
|
375
|
+
await withClient((client) => client.getServiceInstanceLimits(args)),
|
|
376
|
+
),
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
server.registerTool(
|
|
380
|
+
"createRailwayService",
|
|
381
|
+
{
|
|
382
|
+
description:
|
|
383
|
+
"Create a Railway service from a repo, image, template, or as an empty service.",
|
|
384
|
+
inputSchema: {
|
|
385
|
+
projectId: z.string().min(1),
|
|
386
|
+
environmentId: z.string().min(1).optional(),
|
|
387
|
+
name: z.string().min(1).optional(),
|
|
388
|
+
icon: z.string().min(1).optional(),
|
|
389
|
+
branch: z.string().min(1).optional(),
|
|
390
|
+
templateId: z.string().min(1).optional(),
|
|
391
|
+
templateServiceId: z.string().min(1).optional(),
|
|
392
|
+
source: z.unknown().optional(),
|
|
393
|
+
registryCredentials: z.unknown().optional(),
|
|
394
|
+
variables: z.unknown().optional(),
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
async (args) =>
|
|
398
|
+
createToolResult(await withClient((client) => client.createService(args))),
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
server.registerTool(
|
|
402
|
+
"updateRailwayService",
|
|
403
|
+
{
|
|
404
|
+
description:
|
|
405
|
+
"Update a Railway service name or icon.",
|
|
406
|
+
inputSchema: {
|
|
407
|
+
serviceId: z.string().min(1),
|
|
408
|
+
name: z.string().min(1).optional(),
|
|
409
|
+
icon: z.string().min(1).optional(),
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
async (args) =>
|
|
413
|
+
createToolResult(await withClient((client) => client.updateService(args))),
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
server.registerTool(
|
|
417
|
+
"connectRailwayService",
|
|
418
|
+
{
|
|
419
|
+
description:
|
|
420
|
+
"Connect an existing Railway service to a repo or image source.",
|
|
421
|
+
inputSchema: {
|
|
422
|
+
serviceId: z.string().min(1),
|
|
423
|
+
repo: z.string().min(1).optional(),
|
|
424
|
+
image: z.string().min(1).optional(),
|
|
425
|
+
branch: z.string().min(1).optional(),
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
async (args) =>
|
|
429
|
+
createToolResult(await withClient((client) => client.connectService(args))),
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
server.registerTool(
|
|
433
|
+
"disconnectRailwayService",
|
|
434
|
+
{
|
|
435
|
+
description:
|
|
436
|
+
"Disconnect a Railway service from its current source.",
|
|
437
|
+
inputSchema: {
|
|
438
|
+
serviceId: z.string().min(1),
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
async ({ serviceId }) =>
|
|
442
|
+
createToolResult(
|
|
443
|
+
await withClient((client) => client.disconnectService(serviceId)),
|
|
444
|
+
),
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
server.registerTool(
|
|
448
|
+
"deleteRailwayService",
|
|
449
|
+
{
|
|
450
|
+
description:
|
|
451
|
+
"Delete a Railway service. Destructive.",
|
|
452
|
+
inputSchema: {
|
|
453
|
+
serviceId: z.string().min(1),
|
|
454
|
+
environmentId: z.string().min(1).optional(),
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
async (args) =>
|
|
458
|
+
createToolResult(await withClient((client) => client.deleteService(args))),
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
server.registerTool(
|
|
462
|
+
"updateRailwayServiceInstance",
|
|
463
|
+
{
|
|
464
|
+
description:
|
|
465
|
+
"Update build, deploy, region, healthcheck, cron, or source settings for a Railway service instance.",
|
|
466
|
+
inputSchema: {
|
|
467
|
+
serviceId: z.string().min(1),
|
|
468
|
+
environmentId: z.string().min(1).optional(),
|
|
469
|
+
buildCommand: z.string().optional(),
|
|
470
|
+
builder: z.string().min(1).optional(),
|
|
471
|
+
cronSchedule: z.string().optional(),
|
|
472
|
+
dockerfilePath: z.string().optional(),
|
|
473
|
+
drainingSeconds: z.number().int().min(0).optional(),
|
|
474
|
+
healthcheckPath: z.string().optional(),
|
|
475
|
+
healthcheckTimeout: z.number().int().min(0).optional(),
|
|
476
|
+
ipv6EgressEnabled: z.boolean().optional(),
|
|
477
|
+
multiRegionConfig: z.unknown().optional(),
|
|
478
|
+
nixpacksPlan: z.unknown().optional(),
|
|
479
|
+
numReplicas: z.number().int().min(0).optional(),
|
|
480
|
+
overlapSeconds: z.number().int().min(0).optional(),
|
|
481
|
+
preDeployCommand: z.array(z.string()).optional(),
|
|
482
|
+
railwayConfigFile: z.string().optional(),
|
|
483
|
+
region: z.string().optional(),
|
|
484
|
+
registryCredentials: z.unknown().optional(),
|
|
485
|
+
restartPolicyMaxRetries: z.number().int().min(0).optional(),
|
|
486
|
+
restartPolicyType: z.string().min(1).optional(),
|
|
487
|
+
rootDirectory: z.string().optional(),
|
|
488
|
+
sleepApplication: z.boolean().optional(),
|
|
489
|
+
source: z.unknown().optional(),
|
|
490
|
+
startCommand: z.string().optional(),
|
|
491
|
+
watchPatterns: z.array(z.string()).optional(),
|
|
492
|
+
},
|
|
493
|
+
},
|
|
494
|
+
async (args) =>
|
|
495
|
+
createToolResult(
|
|
496
|
+
await withClient((client) => client.updateServiceInstance(args)),
|
|
497
|
+
),
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
server.registerTool(
|
|
501
|
+
"deployRailwayService",
|
|
502
|
+
{
|
|
503
|
+
description:
|
|
504
|
+
"Trigger a Railway deployment for a service instance.",
|
|
505
|
+
inputSchema: {
|
|
506
|
+
serviceId: z.string().min(1),
|
|
507
|
+
environmentId: z.string().min(1),
|
|
508
|
+
commitSha: z.string().min(1).optional(),
|
|
509
|
+
latestCommit: z.boolean().optional(),
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
async (args) =>
|
|
513
|
+
createToolResult(await withClient((client) => client.deployService(args))),
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
server.registerTool(
|
|
517
|
+
"redeployRailwayService",
|
|
518
|
+
{
|
|
519
|
+
description:
|
|
520
|
+
"Redeploy the latest Railway deployment for a service instance.",
|
|
521
|
+
inputSchema: {
|
|
522
|
+
serviceId: z.string().min(1),
|
|
523
|
+
environmentId: z.string().min(1),
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
async (args) =>
|
|
527
|
+
createToolResult(await withClient((client) => client.redeployService(args))),
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
server.registerTool(
|
|
531
|
+
"updateRailwayServiceInstanceLimits",
|
|
532
|
+
{
|
|
533
|
+
description:
|
|
534
|
+
"Update vCPU or memory limits for a Railway service instance.",
|
|
535
|
+
inputSchema: {
|
|
536
|
+
serviceId: z.string().min(1),
|
|
537
|
+
environmentId: z.string().min(1),
|
|
538
|
+
memoryGB: z.number().positive().optional(),
|
|
539
|
+
vCPUs: z.number().positive().optional(),
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
async (args) =>
|
|
543
|
+
createToolResult(
|
|
544
|
+
await withClient((client) => client.updateServiceInstanceLimits(args)),
|
|
545
|
+
),
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
server.registerTool(
|
|
549
|
+
"getRailwayDeployment",
|
|
550
|
+
{
|
|
551
|
+
description: "Inspect one Railway deployment.",
|
|
552
|
+
inputSchema: {
|
|
553
|
+
deploymentId: z.string().min(1),
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
async ({ deploymentId }) =>
|
|
557
|
+
createToolResult(
|
|
558
|
+
await withClient((client) => client.getDeployment(deploymentId)),
|
|
559
|
+
),
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
server.registerTool(
|
|
563
|
+
"listRailwayDeployments",
|
|
564
|
+
{
|
|
565
|
+
description:
|
|
566
|
+
"List Railway deployments for a project, environment, or service.",
|
|
567
|
+
inputSchema: {
|
|
568
|
+
projectId: z.string().min(1).optional(),
|
|
569
|
+
environmentId: z.string().min(1).optional(),
|
|
570
|
+
serviceId: z.string().min(1).optional(),
|
|
571
|
+
first: z.number().int().min(1).max(100).optional(),
|
|
572
|
+
after: z.string().min(1).optional(),
|
|
573
|
+
before: z.string().min(1).optional(),
|
|
574
|
+
last: z.number().int().min(1).max(100).optional(),
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
async (args = {}) =>
|
|
578
|
+
createToolResult(await withClient((client) => client.listDeployments(args))),
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
server.registerTool(
|
|
582
|
+
"upsertRailwayVariable",
|
|
583
|
+
{
|
|
584
|
+
description:
|
|
585
|
+
"Create or update a Railway variable.",
|
|
586
|
+
inputSchema: {
|
|
587
|
+
projectId: z.string().min(1),
|
|
588
|
+
environmentId: z.string().min(1),
|
|
589
|
+
name: z.string().min(1),
|
|
590
|
+
value: z.string(),
|
|
591
|
+
serviceId: z.string().min(1).optional(),
|
|
592
|
+
skipDeploys: z.boolean().optional(),
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
async (args) =>
|
|
596
|
+
createToolResult(await withClient((client) => client.upsertVariable(args))),
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
server.registerTool(
|
|
600
|
+
"deleteRailwayVariable",
|
|
601
|
+
{
|
|
602
|
+
description:
|
|
603
|
+
"Delete a Railway variable.",
|
|
604
|
+
inputSchema: {
|
|
605
|
+
projectId: z.string().min(1),
|
|
606
|
+
environmentId: z.string().min(1),
|
|
607
|
+
name: z.string().min(1),
|
|
608
|
+
serviceId: z.string().min(1).optional(),
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
async (args) =>
|
|
612
|
+
createToolResult(await withClient((client) => client.deleteVariable(args))),
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
server.registerTool(
|
|
616
|
+
"createRailwayServiceDomain",
|
|
617
|
+
{
|
|
618
|
+
description:
|
|
619
|
+
"Create a Railway-managed service domain.",
|
|
620
|
+
inputSchema: {
|
|
621
|
+
serviceId: z.string().min(1),
|
|
622
|
+
environmentId: z.string().min(1),
|
|
623
|
+
targetPort: z.number().int().min(1).optional(),
|
|
624
|
+
},
|
|
625
|
+
},
|
|
626
|
+
async (args) =>
|
|
627
|
+
createToolResult(
|
|
628
|
+
await withClient((client) => client.createServiceDomain(args)),
|
|
629
|
+
),
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
server.registerTool(
|
|
633
|
+
"updateRailwayServiceDomain",
|
|
634
|
+
{
|
|
635
|
+
description:
|
|
636
|
+
"Update a Railway-managed service domain target port or domain binding.",
|
|
637
|
+
inputSchema: {
|
|
638
|
+
serviceDomainId: z.string().min(1),
|
|
639
|
+
serviceId: z.string().min(1),
|
|
640
|
+
environmentId: z.string().min(1),
|
|
641
|
+
domain: z.string().min(1),
|
|
642
|
+
targetPort: z.number().int().min(1).optional(),
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
async (args) =>
|
|
646
|
+
createToolResult(
|
|
647
|
+
await withClient((client) => client.updateServiceDomain(args)),
|
|
648
|
+
),
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
server.registerTool(
|
|
652
|
+
"deleteRailwayServiceDomain",
|
|
653
|
+
{
|
|
654
|
+
description:
|
|
655
|
+
"Delete a Railway-managed service domain.",
|
|
656
|
+
inputSchema: {
|
|
657
|
+
serviceDomainId: z.string().min(1),
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
async ({ serviceDomainId }) =>
|
|
661
|
+
createToolResult(
|
|
662
|
+
await withClient((client) => client.deleteServiceDomain(serviceDomainId)),
|
|
663
|
+
),
|
|
664
|
+
);
|
|
665
|
+
|
|
666
|
+
server.registerTool(
|
|
667
|
+
"createRailwayCustomDomain",
|
|
668
|
+
{
|
|
669
|
+
description:
|
|
670
|
+
"Add a custom domain to a Railway service.",
|
|
671
|
+
inputSchema: {
|
|
672
|
+
projectId: z.string().min(1),
|
|
673
|
+
environmentId: z.string().min(1),
|
|
674
|
+
serviceId: z.string().min(1),
|
|
675
|
+
domain: z.string().min(1),
|
|
676
|
+
targetPort: z.number().int().min(1).optional(),
|
|
677
|
+
},
|
|
678
|
+
},
|
|
679
|
+
async (args) =>
|
|
680
|
+
createToolResult(
|
|
681
|
+
await withClient((client) => client.createCustomDomain(args)),
|
|
682
|
+
),
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
server.registerTool(
|
|
686
|
+
"updateRailwayCustomDomain",
|
|
687
|
+
{
|
|
688
|
+
description:
|
|
689
|
+
"Update a custom Railway domain target port.",
|
|
690
|
+
inputSchema: {
|
|
691
|
+
customDomainId: z.string().min(1),
|
|
692
|
+
environmentId: z.string().min(1),
|
|
693
|
+
targetPort: z.number().int().min(1).optional(),
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
async (args) =>
|
|
697
|
+
createToolResult(
|
|
698
|
+
await withClient((client) => client.updateCustomDomain(args)),
|
|
699
|
+
),
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
server.registerTool(
|
|
703
|
+
"deleteRailwayCustomDomain",
|
|
704
|
+
{
|
|
705
|
+
description:
|
|
706
|
+
"Delete a custom Railway domain.",
|
|
707
|
+
inputSchema: {
|
|
708
|
+
customDomainId: z.string().min(1),
|
|
709
|
+
},
|
|
710
|
+
},
|
|
711
|
+
async ({ customDomainId }) =>
|
|
712
|
+
createToolResult(
|
|
713
|
+
await withClient((client) => client.deleteCustomDomain(customDomainId)),
|
|
714
|
+
),
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
server.registerTool(
|
|
718
|
+
"createRailwayVolume",
|
|
719
|
+
{
|
|
720
|
+
description:
|
|
721
|
+
"Create a Railway volume.",
|
|
722
|
+
inputSchema: {
|
|
723
|
+
projectId: z.string().min(1),
|
|
724
|
+
mountPath: z.string().min(1),
|
|
725
|
+
environmentId: z.string().min(1).optional(),
|
|
726
|
+
serviceId: z.string().min(1).optional(),
|
|
727
|
+
region: z.string().min(1).optional(),
|
|
728
|
+
},
|
|
729
|
+
},
|
|
730
|
+
async (args) =>
|
|
731
|
+
createToolResult(await withClient((client) => client.createVolume(args))),
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
server.registerTool(
|
|
735
|
+
"deleteRailwayVolume",
|
|
736
|
+
{
|
|
737
|
+
description:
|
|
738
|
+
"Delete a Railway volume. Destructive.",
|
|
739
|
+
inputSchema: {
|
|
740
|
+
volumeId: z.string().min(1),
|
|
741
|
+
},
|
|
742
|
+
},
|
|
743
|
+
async ({ volumeId }) =>
|
|
744
|
+
createToolResult(await withClient((client) => client.deleteVolume(volumeId))),
|
|
745
|
+
);
|
|
746
|
+
|
|
217
747
|
server.registerTool(
|
|
218
748
|
"doctorRailwayProject",
|
|
219
749
|
{
|