@masonator/coolify-mcp 0.8.1 → 1.0.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 +38 -14
- package/dist/__tests__/coolify-client.test.js +288 -0
- package/dist/lib/coolify-client.d.ts +29 -1
- package/dist/lib/coolify-client.js +113 -0
- package/dist/lib/mcp-server.d.ts +1 -0
- package/dist/lib/mcp-server.js +264 -2
- package/dist/types/coolify.d.ts +16 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,20 +6,35 @@ A Model Context Protocol (MCP) server for [Coolify](https://coolify.io/), enabli
|
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
This MCP server provides **
|
|
10
|
-
|
|
11
|
-
| Category
|
|
12
|
-
|
|
|
13
|
-
| **Infrastructure**
|
|
14
|
-
| **Diagnostics**
|
|
15
|
-
| **
|
|
16
|
-
| **
|
|
17
|
-
| **
|
|
18
|
-
| **
|
|
19
|
-
| **
|
|
20
|
-
| **
|
|
21
|
-
| **
|
|
22
|
-
| **
|
|
9
|
+
This MCP server provides **65 tools** and **7 workflow prompts** for **debugging, management, and deployment**:
|
|
10
|
+
|
|
11
|
+
| Category | Tools |
|
|
12
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| **Infrastructure** | overview (all resources at once) |
|
|
14
|
+
| **Diagnostics** | diagnose_app, diagnose_server, find_issues (smart lookup by name/domain/IP) |
|
|
15
|
+
| **Batch Operations** | restart_project_apps, bulk_env_update, stop_all_apps, redeploy_project |
|
|
16
|
+
| **Servers** | list, get, validate, resources, domains |
|
|
17
|
+
| **Projects** | list, get, create, update, delete |
|
|
18
|
+
| **Environments** | list, get, create, delete |
|
|
19
|
+
| **Applications** | list, get, update, delete, start, stop, restart, logs, env vars (CRUD), create (private-gh, private-key) |
|
|
20
|
+
| **Databases** | list, get, start, stop, restart, backups (list, get), backup executions (list, get) |
|
|
21
|
+
| **Services** | list, get, create, update, delete, start, stop, restart, env vars (list, create, delete) |
|
|
22
|
+
| **Deployments** | list, get, deploy, cancel, list by application |
|
|
23
|
+
| **Private Keys** | list, get, create, update, delete |
|
|
24
|
+
|
|
25
|
+
### Workflow Prompts
|
|
26
|
+
|
|
27
|
+
Pre-built guided workflows that walk you through common tasks:
|
|
28
|
+
|
|
29
|
+
| Prompt | Description |
|
|
30
|
+
| ------------------ | ----------------------------------------------------------- |
|
|
31
|
+
| `debug-app` | Debug an application - gathers logs, status, env vars |
|
|
32
|
+
| `health-check` | Full infrastructure health analysis |
|
|
33
|
+
| `deploy-app` | Step-by-step deployment wizard from Git repository |
|
|
34
|
+
| `troubleshoot-ssl` | SSL/TLS certificate diagnosis workflow |
|
|
35
|
+
| `restart-project` | Safely restart all apps in a project with status monitoring |
|
|
36
|
+
| `env-audit` | Audit and compare environment variables across applications |
|
|
37
|
+
| `backup-status` | Check database backup status and history |
|
|
23
38
|
|
|
24
39
|
## Installation
|
|
25
40
|
|
|
@@ -271,6 +286,15 @@ These tools accept human-friendly identifiers instead of just UUIDs:
|
|
|
271
286
|
- `update_private_key` - Update a private key
|
|
272
287
|
- `delete_private_key` - Delete a private key
|
|
273
288
|
|
|
289
|
+
### Batch Operations
|
|
290
|
+
|
|
291
|
+
Power user tools for operating on multiple resources at once:
|
|
292
|
+
|
|
293
|
+
- `restart_project_apps` - Restart all applications in a project
|
|
294
|
+
- `bulk_env_update` - Update or create an environment variable across multiple applications (upsert behavior)
|
|
295
|
+
- `stop_all_apps` - Emergency stop all running applications (requires confirmation)
|
|
296
|
+
- `redeploy_project` - Redeploy all applications in a project with force rebuild
|
|
297
|
+
|
|
274
298
|
## Contributing
|
|
275
299
|
|
|
276
300
|
Contributions welcome! Please open an issue first to discuss major changes.
|
|
@@ -1669,4 +1669,292 @@ describe('CoolifyClient', () => {
|
|
|
1669
1669
|
});
|
|
1670
1670
|
});
|
|
1671
1671
|
});
|
|
1672
|
+
// ===========================================================================
|
|
1673
|
+
// Batch Operations Tests
|
|
1674
|
+
// ===========================================================================
|
|
1675
|
+
describe('Batch Operations', () => {
|
|
1676
|
+
describe('restartProjectApps', () => {
|
|
1677
|
+
const mockApps = [
|
|
1678
|
+
{
|
|
1679
|
+
id: 1,
|
|
1680
|
+
uuid: 'app-1',
|
|
1681
|
+
name: 'app-one',
|
|
1682
|
+
project_uuid: 'proj-1',
|
|
1683
|
+
status: 'running',
|
|
1684
|
+
created_at: '2024-01-01',
|
|
1685
|
+
updated_at: '2024-01-01',
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
id: 2,
|
|
1689
|
+
uuid: 'app-2',
|
|
1690
|
+
name: 'app-two',
|
|
1691
|
+
project_uuid: 'proj-1',
|
|
1692
|
+
status: 'running',
|
|
1693
|
+
created_at: '2024-01-01',
|
|
1694
|
+
updated_at: '2024-01-01',
|
|
1695
|
+
},
|
|
1696
|
+
{
|
|
1697
|
+
id: 3,
|
|
1698
|
+
uuid: 'app-3',
|
|
1699
|
+
name: 'app-three',
|
|
1700
|
+
project_uuid: 'proj-2', // Different project
|
|
1701
|
+
status: 'running',
|
|
1702
|
+
created_at: '2024-01-01',
|
|
1703
|
+
updated_at: '2024-01-01',
|
|
1704
|
+
},
|
|
1705
|
+
];
|
|
1706
|
+
it('should restart all apps in a project', async () => {
|
|
1707
|
+
mockFetch
|
|
1708
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1709
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Restarted' })) // app-1
|
|
1710
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Restarted' })); // app-2
|
|
1711
|
+
const result = await client.restartProjectApps('proj-1');
|
|
1712
|
+
expect(result.summary.total).toBe(2);
|
|
1713
|
+
expect(result.summary.succeeded).toBe(2);
|
|
1714
|
+
expect(result.summary.failed).toBe(0);
|
|
1715
|
+
expect(result.succeeded).toEqual([
|
|
1716
|
+
{ uuid: 'app-1', name: 'app-one' },
|
|
1717
|
+
{ uuid: 'app-2', name: 'app-two' },
|
|
1718
|
+
]);
|
|
1719
|
+
expect(result.failed).toEqual([]);
|
|
1720
|
+
});
|
|
1721
|
+
it('should handle partial failures gracefully', async () => {
|
|
1722
|
+
mockFetch
|
|
1723
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1724
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Restarted' }))
|
|
1725
|
+
.mockRejectedValueOnce(new Error('App not running'));
|
|
1726
|
+
const result = await client.restartProjectApps('proj-1');
|
|
1727
|
+
expect(result.summary.succeeded).toBe(1);
|
|
1728
|
+
expect(result.summary.failed).toBe(1);
|
|
1729
|
+
expect(result.succeeded).toHaveLength(1);
|
|
1730
|
+
expect(result.failed).toHaveLength(1);
|
|
1731
|
+
expect(result.failed[0].error).toBe('App not running');
|
|
1732
|
+
});
|
|
1733
|
+
it('should return empty result for empty project', async () => {
|
|
1734
|
+
mockFetch.mockResolvedValueOnce(mockResponse([]));
|
|
1735
|
+
const result = await client.restartProjectApps('empty-project');
|
|
1736
|
+
expect(result.summary.total).toBe(0);
|
|
1737
|
+
expect(result.summary.succeeded).toBe(0);
|
|
1738
|
+
expect(result.summary.failed).toBe(0);
|
|
1739
|
+
});
|
|
1740
|
+
it('should return empty result for project with no apps', async () => {
|
|
1741
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockApps));
|
|
1742
|
+
const result = await client.restartProjectApps('nonexistent-project');
|
|
1743
|
+
expect(result.summary.total).toBe(0);
|
|
1744
|
+
});
|
|
1745
|
+
});
|
|
1746
|
+
describe('bulkEnvUpdate', () => {
|
|
1747
|
+
const mockApps = [
|
|
1748
|
+
{
|
|
1749
|
+
id: 1,
|
|
1750
|
+
uuid: 'app-1',
|
|
1751
|
+
name: 'app-one',
|
|
1752
|
+
status: 'running',
|
|
1753
|
+
created_at: '2024-01-01',
|
|
1754
|
+
updated_at: '2024-01-01',
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
id: 2,
|
|
1758
|
+
uuid: 'app-2',
|
|
1759
|
+
name: 'app-two',
|
|
1760
|
+
status: 'running',
|
|
1761
|
+
created_at: '2024-01-01',
|
|
1762
|
+
updated_at: '2024-01-01',
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
id: 3,
|
|
1766
|
+
uuid: 'app-3',
|
|
1767
|
+
name: 'app-three',
|
|
1768
|
+
status: 'running',
|
|
1769
|
+
created_at: '2024-01-01',
|
|
1770
|
+
updated_at: '2024-01-01',
|
|
1771
|
+
},
|
|
1772
|
+
];
|
|
1773
|
+
it('should update env var across multiple apps', async () => {
|
|
1774
|
+
mockFetch
|
|
1775
|
+
.mockResolvedValueOnce(mockResponse(mockApps)) // listApplications
|
|
1776
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Updated' })) // app-1
|
|
1777
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Updated' })); // app-2
|
|
1778
|
+
const result = await client.bulkEnvUpdate(['app-1', 'app-2'], 'API_KEY', 'new-value');
|
|
1779
|
+
expect(result.summary.total).toBe(2);
|
|
1780
|
+
expect(result.summary.succeeded).toBe(2);
|
|
1781
|
+
expect(result.summary.failed).toBe(0);
|
|
1782
|
+
expect(result.succeeded).toEqual([
|
|
1783
|
+
{ uuid: 'app-1', name: 'app-one' },
|
|
1784
|
+
{ uuid: 'app-2', name: 'app-two' },
|
|
1785
|
+
]);
|
|
1786
|
+
});
|
|
1787
|
+
it('should handle partial failures', async () => {
|
|
1788
|
+
mockFetch
|
|
1789
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1790
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Updated' }))
|
|
1791
|
+
.mockRejectedValueOnce(new Error('App not found'));
|
|
1792
|
+
const result = await client.bulkEnvUpdate(['app-1', 'app-2'], 'API_KEY', 'new-value');
|
|
1793
|
+
expect(result.summary.succeeded).toBe(1);
|
|
1794
|
+
expect(result.summary.failed).toBe(1);
|
|
1795
|
+
expect(result.failed[0].error).toBe('App not found');
|
|
1796
|
+
});
|
|
1797
|
+
it('should handle unknown app UUIDs gracefully', async () => {
|
|
1798
|
+
mockFetch
|
|
1799
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1800
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Updated' }))
|
|
1801
|
+
.mockRejectedValueOnce(new Error('Application not found'));
|
|
1802
|
+
const result = await client.bulkEnvUpdate(['app-1', 'unknown-app'], 'API_KEY', 'new-value');
|
|
1803
|
+
expect(result.summary.total).toBe(2);
|
|
1804
|
+
expect(result.summary.succeeded).toBe(1);
|
|
1805
|
+
expect(result.summary.failed).toBe(1);
|
|
1806
|
+
expect(result.succeeded[0].uuid).toBe('app-1');
|
|
1807
|
+
expect(result.failed[0].uuid).toBe('unknown-app');
|
|
1808
|
+
expect(result.failed[0].error).toBe('Application not found');
|
|
1809
|
+
});
|
|
1810
|
+
it('should return empty result for empty app UUIDs array', async () => {
|
|
1811
|
+
const result = await client.bulkEnvUpdate([], 'API_KEY', 'new-value');
|
|
1812
|
+
expect(result.summary.total).toBe(0);
|
|
1813
|
+
expect(result.summary.succeeded).toBe(0);
|
|
1814
|
+
expect(result.summary.failed).toBe(0);
|
|
1815
|
+
// No API calls should be made
|
|
1816
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
1817
|
+
});
|
|
1818
|
+
it('should send build time flag when specified', async () => {
|
|
1819
|
+
mockFetch
|
|
1820
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1821
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Updated' }));
|
|
1822
|
+
await client.bulkEnvUpdate(['app-1'], 'BUILD_VAR', 'value', true);
|
|
1823
|
+
// Verify the PATCH call was made with is_build_time
|
|
1824
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-1/envs', expect.objectContaining({
|
|
1825
|
+
method: 'PATCH',
|
|
1826
|
+
body: JSON.stringify({ key: 'BUILD_VAR', value: 'value', is_build_time: true }),
|
|
1827
|
+
}));
|
|
1828
|
+
});
|
|
1829
|
+
});
|
|
1830
|
+
describe('stopAllApps', () => {
|
|
1831
|
+
const mockApps = [
|
|
1832
|
+
{
|
|
1833
|
+
id: 1,
|
|
1834
|
+
uuid: 'app-1',
|
|
1835
|
+
name: 'running-app',
|
|
1836
|
+
status: 'running:healthy',
|
|
1837
|
+
created_at: '2024-01-01',
|
|
1838
|
+
updated_at: '2024-01-01',
|
|
1839
|
+
},
|
|
1840
|
+
{
|
|
1841
|
+
id: 2,
|
|
1842
|
+
uuid: 'app-2',
|
|
1843
|
+
name: 'healthy-app',
|
|
1844
|
+
status: 'healthy',
|
|
1845
|
+
created_at: '2024-01-01',
|
|
1846
|
+
updated_at: '2024-01-01',
|
|
1847
|
+
},
|
|
1848
|
+
{
|
|
1849
|
+
id: 3,
|
|
1850
|
+
uuid: 'app-3',
|
|
1851
|
+
name: 'stopped-app',
|
|
1852
|
+
status: 'exited',
|
|
1853
|
+
created_at: '2024-01-01',
|
|
1854
|
+
updated_at: '2024-01-01',
|
|
1855
|
+
},
|
|
1856
|
+
];
|
|
1857
|
+
it('should stop all running apps', async () => {
|
|
1858
|
+
mockFetch
|
|
1859
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1860
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Stopped' })) // app-1
|
|
1861
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Stopped' })); // app-2
|
|
1862
|
+
const result = await client.stopAllApps();
|
|
1863
|
+
// Only 2 apps are running (app-1 and app-2), app-3 is already stopped
|
|
1864
|
+
expect(result.summary.total).toBe(2);
|
|
1865
|
+
expect(result.summary.succeeded).toBe(2);
|
|
1866
|
+
expect(result.summary.failed).toBe(0);
|
|
1867
|
+
});
|
|
1868
|
+
it('should handle partial failures', async () => {
|
|
1869
|
+
mockFetch
|
|
1870
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1871
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Stopped' }))
|
|
1872
|
+
.mockRejectedValueOnce(new Error('Failed to stop'));
|
|
1873
|
+
const result = await client.stopAllApps();
|
|
1874
|
+
expect(result.summary.succeeded).toBe(1);
|
|
1875
|
+
expect(result.summary.failed).toBe(1);
|
|
1876
|
+
});
|
|
1877
|
+
it('should return empty result when no running apps', async () => {
|
|
1878
|
+
const stoppedApps = [
|
|
1879
|
+
{ ...mockApps[2] }, // Only the stopped app
|
|
1880
|
+
];
|
|
1881
|
+
mockFetch.mockResolvedValueOnce(mockResponse(stoppedApps));
|
|
1882
|
+
const result = await client.stopAllApps();
|
|
1883
|
+
expect(result.summary.total).toBe(0);
|
|
1884
|
+
});
|
|
1885
|
+
});
|
|
1886
|
+
describe('redeployProjectApps', () => {
|
|
1887
|
+
const mockApps = [
|
|
1888
|
+
{
|
|
1889
|
+
id: 1,
|
|
1890
|
+
uuid: 'app-1',
|
|
1891
|
+
name: 'app-one',
|
|
1892
|
+
project_uuid: 'proj-1',
|
|
1893
|
+
status: 'running',
|
|
1894
|
+
created_at: '2024-01-01',
|
|
1895
|
+
updated_at: '2024-01-01',
|
|
1896
|
+
},
|
|
1897
|
+
{
|
|
1898
|
+
id: 2,
|
|
1899
|
+
uuid: 'app-2',
|
|
1900
|
+
name: 'app-two',
|
|
1901
|
+
project_uuid: 'proj-1',
|
|
1902
|
+
status: 'running',
|
|
1903
|
+
created_at: '2024-01-01',
|
|
1904
|
+
updated_at: '2024-01-01',
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
id: 3,
|
|
1908
|
+
uuid: 'app-3',
|
|
1909
|
+
name: 'app-three',
|
|
1910
|
+
project_uuid: 'proj-2', // Different project
|
|
1911
|
+
status: 'running',
|
|
1912
|
+
created_at: '2024-01-01',
|
|
1913
|
+
updated_at: '2024-01-01',
|
|
1914
|
+
},
|
|
1915
|
+
];
|
|
1916
|
+
it('should redeploy all apps in a project', async () => {
|
|
1917
|
+
mockFetch
|
|
1918
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1919
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Deployed' })) // app-1
|
|
1920
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Deployed' })); // app-2
|
|
1921
|
+
const result = await client.redeployProjectApps('proj-1');
|
|
1922
|
+
expect(result.summary.total).toBe(2);
|
|
1923
|
+
expect(result.summary.succeeded).toBe(2);
|
|
1924
|
+
expect(result.summary.failed).toBe(0);
|
|
1925
|
+
});
|
|
1926
|
+
it('should use force=true by default', async () => {
|
|
1927
|
+
mockFetch
|
|
1928
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1929
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Deployed' }))
|
|
1930
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Deployed' }));
|
|
1931
|
+
await client.redeployProjectApps('proj-1');
|
|
1932
|
+
// Verify deploy calls use force=true
|
|
1933
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/deploy?tag=app-1&force=true', expect.any(Object));
|
|
1934
|
+
});
|
|
1935
|
+
it('should support force=false', async () => {
|
|
1936
|
+
mockFetch
|
|
1937
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1938
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Deployed' }))
|
|
1939
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Deployed' }));
|
|
1940
|
+
await client.redeployProjectApps('proj-1', false);
|
|
1941
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/deploy?tag=app-1&force=false', expect.any(Object));
|
|
1942
|
+
});
|
|
1943
|
+
it('should handle partial failures', async () => {
|
|
1944
|
+
mockFetch
|
|
1945
|
+
.mockResolvedValueOnce(mockResponse(mockApps))
|
|
1946
|
+
.mockResolvedValueOnce(mockResponse({ message: 'Deployed' }))
|
|
1947
|
+
.mockRejectedValueOnce(new Error('Build failed'));
|
|
1948
|
+
const result = await client.redeployProjectApps('proj-1');
|
|
1949
|
+
expect(result.summary.succeeded).toBe(1);
|
|
1950
|
+
expect(result.summary.failed).toBe(1);
|
|
1951
|
+
expect(result.failed[0].error).toBe('Build failed');
|
|
1952
|
+
});
|
|
1953
|
+
it('should return empty result for empty project', async () => {
|
|
1954
|
+
mockFetch.mockResolvedValueOnce(mockResponse([]));
|
|
1955
|
+
const result = await client.redeployProjectApps('empty-project');
|
|
1956
|
+
expect(result.summary.total).toBe(0);
|
|
1957
|
+
});
|
|
1958
|
+
});
|
|
1959
|
+
});
|
|
1672
1960
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Coolify API Client
|
|
3
3
|
* Complete HTTP client for the Coolify API v1
|
|
4
4
|
*/
|
|
5
|
-
import type { CoolifyConfig, DeleteOptions, MessageResponse, UuidResponse, Server, ServerResource, ServerDomain, ServerValidation, CreateServerRequest, UpdateServerRequest, Project, CreateProjectRequest, UpdateProjectRequest, Environment, CreateEnvironmentRequest, Application, CreateApplicationPublicRequest, CreateApplicationPrivateGHRequest, CreateApplicationPrivateKeyRequest, CreateApplicationDockerfileRequest, CreateApplicationDockerImageRequest, CreateApplicationDockerComposeRequest, UpdateApplicationRequest, ApplicationActionResponse, EnvironmentVariable, EnvVarSummary, CreateEnvVarRequest, UpdateEnvVarRequest, BulkUpdateEnvVarsRequest, Database, UpdateDatabaseRequest, DatabaseBackup, BackupExecution, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, CloudToken, CreateCloudTokenRequest, UpdateCloudTokenRequest, CloudTokenValidation, Version, ApplicationDiagnostic, ServerDiagnostic, InfrastructureIssuesReport } from '../types/coolify.js';
|
|
5
|
+
import type { CoolifyConfig, DeleteOptions, MessageResponse, UuidResponse, Server, ServerResource, ServerDomain, ServerValidation, CreateServerRequest, UpdateServerRequest, Project, CreateProjectRequest, UpdateProjectRequest, Environment, CreateEnvironmentRequest, Application, CreateApplicationPublicRequest, CreateApplicationPrivateGHRequest, CreateApplicationPrivateKeyRequest, CreateApplicationDockerfileRequest, CreateApplicationDockerImageRequest, CreateApplicationDockerComposeRequest, UpdateApplicationRequest, ApplicationActionResponse, EnvironmentVariable, EnvVarSummary, CreateEnvVarRequest, UpdateEnvVarRequest, BulkUpdateEnvVarsRequest, Database, UpdateDatabaseRequest, DatabaseBackup, BackupExecution, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, CloudToken, CreateCloudTokenRequest, UpdateCloudTokenRequest, CloudTokenValidation, Version, ApplicationDiagnostic, ServerDiagnostic, InfrastructureIssuesReport, BatchOperationResult } from '../types/coolify.js';
|
|
6
6
|
export interface ListOptions {
|
|
7
7
|
page?: number;
|
|
8
8
|
per_page?: number;
|
|
@@ -184,4 +184,32 @@ export declare class CoolifyClient {
|
|
|
184
184
|
* Finds: unreachable servers, unhealthy apps, exited databases, stopped services.
|
|
185
185
|
*/
|
|
186
186
|
findInfrastructureIssues(): Promise<InfrastructureIssuesReport>;
|
|
187
|
+
/**
|
|
188
|
+
* Aggregate results from Promise.allSettled into a BatchOperationResult.
|
|
189
|
+
*/
|
|
190
|
+
private aggregateBatchResults;
|
|
191
|
+
/**
|
|
192
|
+
* Restart all applications in a project.
|
|
193
|
+
* @param projectUuid - Project UUID
|
|
194
|
+
*/
|
|
195
|
+
restartProjectApps(projectUuid: string): Promise<BatchOperationResult>;
|
|
196
|
+
/**
|
|
197
|
+
* Update or create an environment variable across multiple applications.
|
|
198
|
+
* Uses upsert behavior: creates if not exists, updates if exists.
|
|
199
|
+
* @param appUuids - Array of application UUIDs
|
|
200
|
+
* @param key - Environment variable key
|
|
201
|
+
* @param value - Environment variable value
|
|
202
|
+
* @param isBuildTime - Whether this is a build-time variable (default: false)
|
|
203
|
+
*/
|
|
204
|
+
bulkEnvUpdate(appUuids: string[], key: string, value: string, isBuildTime?: boolean): Promise<BatchOperationResult>;
|
|
205
|
+
/**
|
|
206
|
+
* Emergency stop all running applications across entire infrastructure.
|
|
207
|
+
*/
|
|
208
|
+
stopAllApps(): Promise<BatchOperationResult>;
|
|
209
|
+
/**
|
|
210
|
+
* Redeploy all applications in a project.
|
|
211
|
+
* @param projectUuid - Project UUID
|
|
212
|
+
* @param force - Force rebuild (default: true)
|
|
213
|
+
*/
|
|
214
|
+
redeployProjectApps(projectUuid: string, force?: boolean): Promise<BatchOperationResult>;
|
|
187
215
|
}
|
|
@@ -984,4 +984,117 @@ export class CoolifyClient {
|
|
|
984
984
|
...(errors.length > 0 && { errors }),
|
|
985
985
|
};
|
|
986
986
|
}
|
|
987
|
+
// ===========================================================================
|
|
988
|
+
// Batch Operations
|
|
989
|
+
// ===========================================================================
|
|
990
|
+
/**
|
|
991
|
+
* Aggregate results from Promise.allSettled into a BatchOperationResult.
|
|
992
|
+
*/
|
|
993
|
+
aggregateBatchResults(resources, results) {
|
|
994
|
+
const succeeded = [];
|
|
995
|
+
const failed = [];
|
|
996
|
+
results.forEach((result, index) => {
|
|
997
|
+
const resource = resources[index];
|
|
998
|
+
const name = resource.name || resource.uuid;
|
|
999
|
+
if (result.status === 'fulfilled') {
|
|
1000
|
+
succeeded.push({ uuid: resource.uuid, name });
|
|
1001
|
+
}
|
|
1002
|
+
else {
|
|
1003
|
+
const error = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
1004
|
+
failed.push({ uuid: resource.uuid, name, error });
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
return {
|
|
1008
|
+
summary: {
|
|
1009
|
+
total: resources.length,
|
|
1010
|
+
succeeded: succeeded.length,
|
|
1011
|
+
failed: failed.length,
|
|
1012
|
+
},
|
|
1013
|
+
succeeded,
|
|
1014
|
+
failed,
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Restart all applications in a project.
|
|
1019
|
+
* @param projectUuid - Project UUID
|
|
1020
|
+
*/
|
|
1021
|
+
async restartProjectApps(projectUuid) {
|
|
1022
|
+
const allApps = (await this.listApplications());
|
|
1023
|
+
const projectApps = allApps.filter((app) => app.project_uuid === projectUuid);
|
|
1024
|
+
if (projectApps.length === 0) {
|
|
1025
|
+
return {
|
|
1026
|
+
summary: { total: 0, succeeded: 0, failed: 0 },
|
|
1027
|
+
succeeded: [],
|
|
1028
|
+
failed: [],
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
const results = await Promise.allSettled(projectApps.map((app) => this.restartApplication(app.uuid)));
|
|
1032
|
+
return this.aggregateBatchResults(projectApps, results);
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Update or create an environment variable across multiple applications.
|
|
1036
|
+
* Uses upsert behavior: creates if not exists, updates if exists.
|
|
1037
|
+
* @param appUuids - Array of application UUIDs
|
|
1038
|
+
* @param key - Environment variable key
|
|
1039
|
+
* @param value - Environment variable value
|
|
1040
|
+
* @param isBuildTime - Whether this is a build-time variable (default: false)
|
|
1041
|
+
*/
|
|
1042
|
+
async bulkEnvUpdate(appUuids, key, value, isBuildTime = false) {
|
|
1043
|
+
// Early return for empty array - avoid unnecessary API call
|
|
1044
|
+
if (appUuids.length === 0) {
|
|
1045
|
+
return {
|
|
1046
|
+
summary: { total: 0, succeeded: 0, failed: 0 },
|
|
1047
|
+
succeeded: [],
|
|
1048
|
+
failed: [],
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
// Get app names first for better response
|
|
1052
|
+
const allApps = (await this.listApplications());
|
|
1053
|
+
const appMap = new Map(allApps.map((a) => [a.uuid, a.name || a.uuid]));
|
|
1054
|
+
// Build the resource list with names
|
|
1055
|
+
const resources = appUuids.map((uuid) => ({
|
|
1056
|
+
uuid,
|
|
1057
|
+
name: appMap.get(uuid) || uuid,
|
|
1058
|
+
}));
|
|
1059
|
+
const results = await Promise.allSettled(appUuids.map((uuid) => this.updateApplicationEnvVar(uuid, { key, value, is_build_time: isBuildTime })));
|
|
1060
|
+
return this.aggregateBatchResults(resources, results);
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Emergency stop all running applications across entire infrastructure.
|
|
1064
|
+
*/
|
|
1065
|
+
async stopAllApps() {
|
|
1066
|
+
const allApps = (await this.listApplications());
|
|
1067
|
+
// Only stop running apps
|
|
1068
|
+
const runningApps = allApps.filter((app) => {
|
|
1069
|
+
const status = app.status || '';
|
|
1070
|
+
return status.includes('running') || status.includes('healthy');
|
|
1071
|
+
});
|
|
1072
|
+
if (runningApps.length === 0) {
|
|
1073
|
+
return {
|
|
1074
|
+
summary: { total: 0, succeeded: 0, failed: 0 },
|
|
1075
|
+
succeeded: [],
|
|
1076
|
+
failed: [],
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
const results = await Promise.allSettled(runningApps.map((app) => this.stopApplication(app.uuid)));
|
|
1080
|
+
return this.aggregateBatchResults(runningApps, results);
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Redeploy all applications in a project.
|
|
1084
|
+
* @param projectUuid - Project UUID
|
|
1085
|
+
* @param force - Force rebuild (default: true)
|
|
1086
|
+
*/
|
|
1087
|
+
async redeployProjectApps(projectUuid, force = true) {
|
|
1088
|
+
const allApps = (await this.listApplications());
|
|
1089
|
+
const projectApps = allApps.filter((app) => app.project_uuid === projectUuid);
|
|
1090
|
+
if (projectApps.length === 0) {
|
|
1091
|
+
return {
|
|
1092
|
+
summary: { total: 0, succeeded: 0, failed: 0 },
|
|
1093
|
+
succeeded: [],
|
|
1094
|
+
failed: [],
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
const results = await Promise.allSettled(projectApps.map((app) => this.deployByTagOrUuid(app.uuid, force)));
|
|
1098
|
+
return this.aggregateBatchResults(projectApps, results);
|
|
1099
|
+
}
|
|
987
1100
|
}
|
package/dist/lib/mcp-server.d.ts
CHANGED
package/dist/lib/mcp-server.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
21
21
|
import { z } from 'zod';
|
|
22
22
|
import { CoolifyClient, } from './coolify-client.js';
|
|
23
|
-
const VERSION = '0.
|
|
23
|
+
const VERSION = '1.0.0';
|
|
24
24
|
/** Wrap tool handler with consistent error handling */
|
|
25
25
|
function wrapHandler(fn) {
|
|
26
26
|
return fn()
|
|
@@ -44,10 +44,11 @@ export class CoolifyMcpServer extends McpServer {
|
|
|
44
44
|
super({
|
|
45
45
|
name: 'coolify',
|
|
46
46
|
version: VERSION,
|
|
47
|
-
capabilities: { tools: {} },
|
|
47
|
+
capabilities: { tools: {}, prompts: {} },
|
|
48
48
|
});
|
|
49
49
|
this.client = new CoolifyClient(config);
|
|
50
50
|
this.registerTools();
|
|
51
|
+
this.registerPrompts();
|
|
51
52
|
}
|
|
52
53
|
async connect(transport) {
|
|
53
54
|
await super.connect(transport);
|
|
@@ -347,5 +348,266 @@ export class CoolifyMcpServer extends McpServer {
|
|
|
347
348
|
this.tool('diagnose_app', 'Get comprehensive diagnostic info for an application. Accepts UUID, name, or domain (e.g., "stuartmason.co.uk" or "my-app"). Aggregates: status, health assessment, logs (last 50 lines), environment variables (keys only, values hidden), and recent deployments. Use this for debugging application issues.', { query: z.string().describe('Application UUID, name, or domain (FQDN)') }, async ({ query }) => wrapHandler(() => this.client.diagnoseApplication(query)));
|
|
348
349
|
this.tool('diagnose_server', 'Get comprehensive diagnostic info for a server. Accepts UUID, name, or IP address (e.g., "coolify-apps" or "192.168.1.100"). Aggregates: server status, health assessment, running resources, configured domains, and connection validation. Use this for debugging server issues.', { query: z.string().describe('Server UUID, name, or IP address') }, async ({ query }) => wrapHandler(() => this.client.diagnoseServer(query)));
|
|
349
350
|
this.tool('find_issues', 'Scan entire infrastructure for common issues. Finds: unreachable servers, unhealthy/stopped applications, exited databases, and stopped services. Returns a summary with issue counts and detailed list of problems.', {}, async () => wrapHandler(() => this.client.findInfrastructureIssues()));
|
|
351
|
+
// =========================================================================
|
|
352
|
+
// Batch Operations (4 tools) - Operate on multiple resources at once
|
|
353
|
+
// =========================================================================
|
|
354
|
+
this.tool('restart_project_apps', 'Restart all applications in a project. Returns a summary of succeeded/failed restarts with details.', { project_uuid: z.string().describe('Project UUID') }, async ({ project_uuid }) => wrapHandler(() => this.client.restartProjectApps(project_uuid)));
|
|
355
|
+
this.tool('bulk_env_update', 'Update or create an environment variable across multiple applications (upsert behavior). Returns summary of succeeded/failed updates.', {
|
|
356
|
+
app_uuids: z.array(z.string()).describe('Array of application UUIDs'),
|
|
357
|
+
key: z.string().describe('Environment variable key'),
|
|
358
|
+
value: z.string().describe('Environment variable value'),
|
|
359
|
+
is_build_time: z.boolean().optional().describe('Build-time variable (default: false)'),
|
|
360
|
+
}, async ({ app_uuids, key, value, is_build_time }) => wrapHandler(() => this.client.bulkEnvUpdate(app_uuids, key, value, is_build_time)));
|
|
361
|
+
this.tool('stop_all_apps', 'EMERGENCY: Stop ALL running applications across entire infrastructure. Only stops apps that are currently running or healthy. Use with caution!', {
|
|
362
|
+
confirm: z.literal(true).describe('Must be true to confirm this dangerous operation'),
|
|
363
|
+
}, async ({ confirm }) => {
|
|
364
|
+
if (!confirm) {
|
|
365
|
+
return {
|
|
366
|
+
content: [
|
|
367
|
+
{ type: 'text', text: 'Error: Must set confirm=true to stop all apps' },
|
|
368
|
+
],
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
return wrapHandler(() => this.client.stopAllApps());
|
|
372
|
+
});
|
|
373
|
+
this.tool('redeploy_project', 'Redeploy all applications in a project with force rebuild. Returns summary of succeeded/failed deployments.', {
|
|
374
|
+
project_uuid: z.string().describe('Project UUID'),
|
|
375
|
+
force: z.boolean().optional().describe('Force rebuild (default: true)'),
|
|
376
|
+
}, async ({ project_uuid, force }) => wrapHandler(() => this.client.redeployProjectApps(project_uuid, force ?? true)));
|
|
377
|
+
}
|
|
378
|
+
// ===========================================================================
|
|
379
|
+
// Prompt Templates - Guided Workflows
|
|
380
|
+
// ===========================================================================
|
|
381
|
+
registerPrompts() {
|
|
382
|
+
// -------------------------------------------------------------------------
|
|
383
|
+
// debug-app: Comprehensive application debugging workflow
|
|
384
|
+
// -------------------------------------------------------------------------
|
|
385
|
+
this.prompt('debug-app', 'Debug an application - gathers status, logs, env vars, and recent deployments to diagnose issues', {
|
|
386
|
+
query: z
|
|
387
|
+
.string()
|
|
388
|
+
.describe('Application identifier: UUID, name, or domain (e.g., "my-app" or "example.com")'),
|
|
389
|
+
}, async ({ query }) => {
|
|
390
|
+
return {
|
|
391
|
+
messages: [
|
|
392
|
+
{
|
|
393
|
+
role: 'user',
|
|
394
|
+
content: {
|
|
395
|
+
type: 'text',
|
|
396
|
+
text: `I need help debugging my Coolify application "${query}". Please:
|
|
397
|
+
|
|
398
|
+
1. First, use the diagnose_app tool with query="${query}" to get comprehensive diagnostics
|
|
399
|
+
2. Analyze the results and identify:
|
|
400
|
+
- Current health status and any issues
|
|
401
|
+
- Recent deployment failures or errors in logs
|
|
402
|
+
- Missing or misconfigured environment variables
|
|
403
|
+
- Any patterns suggesting the root cause
|
|
404
|
+
3. Provide a clear diagnosis with:
|
|
405
|
+
- What's wrong (if anything)
|
|
406
|
+
- Likely root cause
|
|
407
|
+
- Recommended fix steps
|
|
408
|
+
4. If the app seems healthy, confirm this and suggest any optimizations
|
|
409
|
+
|
|
410
|
+
Start by running diagnose_app now.`,
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
};
|
|
415
|
+
});
|
|
416
|
+
// -------------------------------------------------------------------------
|
|
417
|
+
// health-check: Full infrastructure health analysis
|
|
418
|
+
// -------------------------------------------------------------------------
|
|
419
|
+
this.prompt('health-check', 'Perform a comprehensive health check of your entire Coolify infrastructure', {}, async () => {
|
|
420
|
+
return {
|
|
421
|
+
messages: [
|
|
422
|
+
{
|
|
423
|
+
role: 'user',
|
|
424
|
+
content: {
|
|
425
|
+
type: 'text',
|
|
426
|
+
text: `Please perform a comprehensive health check of my Coolify infrastructure:
|
|
427
|
+
|
|
428
|
+
1. Run find_issues to scan for problems across all servers, apps, databases, and services
|
|
429
|
+
2. Run get_infrastructure_overview to get the full picture
|
|
430
|
+
3. For any issues found, provide:
|
|
431
|
+
- Severity (critical/warning/info)
|
|
432
|
+
- Affected resource and current status
|
|
433
|
+
- Recommended remediation steps
|
|
434
|
+
4. Summarize the overall health:
|
|
435
|
+
- Total resources and their states
|
|
436
|
+
- Any immediate actions needed
|
|
437
|
+
- Preventive recommendations
|
|
438
|
+
|
|
439
|
+
Start by running find_issues now.`,
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
],
|
|
443
|
+
};
|
|
444
|
+
});
|
|
445
|
+
// -------------------------------------------------------------------------
|
|
446
|
+
// deploy-app: Step-by-step deployment wizard
|
|
447
|
+
// -------------------------------------------------------------------------
|
|
448
|
+
this.prompt('deploy-app', 'Step-by-step wizard to deploy a new application from a Git repository', {
|
|
449
|
+
repo: z.string().describe('Git repository URL or org/repo format'),
|
|
450
|
+
branch: z.string().optional().describe('Branch to deploy (default: main)'),
|
|
451
|
+
}, async ({ repo, branch }) => {
|
|
452
|
+
const branchName = branch || 'main';
|
|
453
|
+
return {
|
|
454
|
+
messages: [
|
|
455
|
+
{
|
|
456
|
+
role: 'user',
|
|
457
|
+
content: {
|
|
458
|
+
type: 'text',
|
|
459
|
+
text: `I want to deploy a new application from ${repo} (branch: ${branchName}). Please guide me through the process:
|
|
460
|
+
|
|
461
|
+
1. First, run list_projects to show available projects
|
|
462
|
+
2. Ask me which project to deploy to (or help create a new one)
|
|
463
|
+
3. Run list_servers to show available servers
|
|
464
|
+
4. Ask me which server to deploy on
|
|
465
|
+
5. Run list_private_keys to check available deploy keys
|
|
466
|
+
6. Based on the repository type:
|
|
467
|
+
- If GitHub and we have a GitHub App configured, use create_application_private_gh
|
|
468
|
+
- Otherwise, help set up a deploy key and use create_application_private_key
|
|
469
|
+
7. After creation, ask about:
|
|
470
|
+
- Environment variables needed
|
|
471
|
+
- Domain/FQDN configuration
|
|
472
|
+
- Whether to deploy immediately
|
|
473
|
+
|
|
474
|
+
Start by showing me the available projects.`,
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
],
|
|
478
|
+
};
|
|
479
|
+
});
|
|
480
|
+
// -------------------------------------------------------------------------
|
|
481
|
+
// troubleshoot-ssl: SSL certificate diagnosis workflow
|
|
482
|
+
// -------------------------------------------------------------------------
|
|
483
|
+
this.prompt('troubleshoot-ssl', 'Diagnose SSL/TLS certificate issues for a domain', {
|
|
484
|
+
domain: z.string().describe('Domain having SSL issues (e.g., "example.com")'),
|
|
485
|
+
}, async ({ domain }) => {
|
|
486
|
+
return {
|
|
487
|
+
messages: [
|
|
488
|
+
{
|
|
489
|
+
role: 'user',
|
|
490
|
+
content: {
|
|
491
|
+
type: 'text',
|
|
492
|
+
text: `I'm having SSL/TLS certificate issues with the domain "${domain}". Please help me diagnose:
|
|
493
|
+
|
|
494
|
+
1. First, use diagnose_app with query="${domain}" to find the application
|
|
495
|
+
2. Check the application's FQDN configuration
|
|
496
|
+
3. Look for common SSL issues:
|
|
497
|
+
- Is the domain correctly configured in the FQDN field?
|
|
498
|
+
- Are there any proxy/redirect issues in the logs?
|
|
499
|
+
- Is Let's Encrypt renewal working (check for ACME errors)?
|
|
500
|
+
4. Check the server's domain configuration using get_server_domains
|
|
501
|
+
5. Provide remediation steps:
|
|
502
|
+
- If domain misconfiguration: show how to fix with update_application
|
|
503
|
+
- If SSL renewal issue: suggest checking DNS and Traefik config
|
|
504
|
+
- If proxy issue: suggest checking Traefik labels
|
|
505
|
+
|
|
506
|
+
Start by finding the application for this domain.`,
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
],
|
|
510
|
+
};
|
|
511
|
+
});
|
|
512
|
+
// -------------------------------------------------------------------------
|
|
513
|
+
// restart-project: Safely restart all apps in a project
|
|
514
|
+
// -------------------------------------------------------------------------
|
|
515
|
+
this.prompt('restart-project', 'Safely restart all applications in a project with status monitoring', {
|
|
516
|
+
project: z.string().describe('Project UUID or name'),
|
|
517
|
+
}, async ({ project }) => {
|
|
518
|
+
return {
|
|
519
|
+
messages: [
|
|
520
|
+
{
|
|
521
|
+
role: 'user',
|
|
522
|
+
content: {
|
|
523
|
+
type: 'text',
|
|
524
|
+
text: `I need to restart all applications in the project "${project}". Please handle this safely:
|
|
525
|
+
|
|
526
|
+
1. First, run list_projects to find the project UUID (if a name was given)
|
|
527
|
+
2. Run get_project to confirm the project details and list its environments
|
|
528
|
+
3. Run list_applications to find all apps in this project
|
|
529
|
+
4. Show me a summary of what will be restarted:
|
|
530
|
+
- List each application with current status
|
|
531
|
+
- Warn about any that are already unhealthy
|
|
532
|
+
5. Ask for my confirmation before proceeding
|
|
533
|
+
6. If confirmed, run restart_project_apps with the project UUID
|
|
534
|
+
7. After restart, check the results and report:
|
|
535
|
+
- Which apps restarted successfully
|
|
536
|
+
- Any failures and why
|
|
537
|
+
- Current status of each app
|
|
538
|
+
|
|
539
|
+
Start by finding the project.`,
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
],
|
|
543
|
+
};
|
|
544
|
+
});
|
|
545
|
+
// -------------------------------------------------------------------------
|
|
546
|
+
// env-audit: Audit environment variables across apps
|
|
547
|
+
// -------------------------------------------------------------------------
|
|
548
|
+
this.prompt('env-audit', 'Audit and compare environment variables across applications', {
|
|
549
|
+
apps: z
|
|
550
|
+
.string()
|
|
551
|
+
.optional()
|
|
552
|
+
.describe('Comma-separated app names/UUIDs to audit (optional, defaults to all)'),
|
|
553
|
+
key: z.string().optional().describe('Specific env var key to check across apps'),
|
|
554
|
+
}, async ({ apps, key }) => {
|
|
555
|
+
return {
|
|
556
|
+
messages: [
|
|
557
|
+
{
|
|
558
|
+
role: 'user',
|
|
559
|
+
content: {
|
|
560
|
+
type: 'text',
|
|
561
|
+
text: `Please audit environment variables across my applications${apps ? ` (${apps})` : ''}${key ? ` focusing on the "${key}" variable` : ''}:
|
|
562
|
+
|
|
563
|
+
1. Run list_applications to get the list of apps
|
|
564
|
+
2. For ${apps ? 'the specified apps' : 'each application'}, run list_application_envs
|
|
565
|
+
3. Analyze the environment variables:
|
|
566
|
+
${key ? `- Check if "${key}" is set consistently across all apps` : '- Identify common variables that differ between apps'}
|
|
567
|
+
- Flag any sensitive-looking values that might be exposed
|
|
568
|
+
- Identify missing variables that exist in some apps but not others
|
|
569
|
+
- Check for any empty or placeholder values
|
|
570
|
+
4. Provide a summary:
|
|
571
|
+
- Table showing variable presence across apps
|
|
572
|
+
- Recommendations for standardization
|
|
573
|
+
- Any security concerns
|
|
574
|
+
|
|
575
|
+
Start by listing the applications.`,
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
],
|
|
579
|
+
};
|
|
580
|
+
});
|
|
581
|
+
// -------------------------------------------------------------------------
|
|
582
|
+
// backup-status: Check database backup status
|
|
583
|
+
// -------------------------------------------------------------------------
|
|
584
|
+
this.prompt('backup-status', 'Check backup status and history for all databases', {}, async () => {
|
|
585
|
+
return {
|
|
586
|
+
messages: [
|
|
587
|
+
{
|
|
588
|
+
role: 'user',
|
|
589
|
+
content: {
|
|
590
|
+
type: 'text',
|
|
591
|
+
text: `Please check the backup status of all my databases:
|
|
592
|
+
|
|
593
|
+
1. Run list_databases to get all databases
|
|
594
|
+
2. For each database, run list_database_backups to check scheduled backups
|
|
595
|
+
3. For databases with backups configured, run list_backup_executions to check recent history
|
|
596
|
+
4. Report:
|
|
597
|
+
- Databases WITHOUT any backup schedules (critical!)
|
|
598
|
+
- Last successful backup for each database
|
|
599
|
+
- Any failed backups in the last 7 days
|
|
600
|
+
- Backup frequency and retention settings
|
|
601
|
+
5. Provide recommendations:
|
|
602
|
+
- Which databases need backup configuration
|
|
603
|
+
- Any backup schedules that seem too infrequent
|
|
604
|
+
- Storage concerns if backups are piling up
|
|
605
|
+
|
|
606
|
+
Start by listing all databases.`,
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
};
|
|
611
|
+
});
|
|
350
612
|
}
|
|
351
613
|
}
|
package/dist/types/coolify.d.ts
CHANGED
|
@@ -734,3 +734,19 @@ export interface InfrastructureIssuesReport {
|
|
|
734
734
|
issues: InfrastructureIssue[];
|
|
735
735
|
errors?: string[];
|
|
736
736
|
}
|
|
737
|
+
export interface BatchOperationResult {
|
|
738
|
+
summary: {
|
|
739
|
+
total: number;
|
|
740
|
+
succeeded: number;
|
|
741
|
+
failed: number;
|
|
742
|
+
};
|
|
743
|
+
succeeded: Array<{
|
|
744
|
+
uuid: string;
|
|
745
|
+
name: string;
|
|
746
|
+
}>;
|
|
747
|
+
failed: Array<{
|
|
748
|
+
uuid: string;
|
|
749
|
+
name: string;
|
|
750
|
+
error: string;
|
|
751
|
+
}>;
|
|
752
|
+
}
|