Skip to main content
Glama
apps.ts5.59 kB
import { LiaraClient } from '../api/client.js'; import { Project, CreateProjectRequest, ProjectDetails, Platform, PaginationOptions, paginationToParams, } from '../api/types.js'; import { validateAppName, validateRequired, unwrapApiResponse } from '../utils/errors.js'; /** * List all apps/projects */ export async function listApps( client: LiaraClient, pagination?: PaginationOptions ): Promise<Project[]> { const params = paginationToParams(pagination); const response = await client.get<any>('/v1/projects', params); return unwrapApiResponse<Project[]>(response, ['projects', 'data', 'items']); } /** * Get details of a specific app */ export async function getApp( client: LiaraClient, name: string ): Promise<ProjectDetails> { validateAppName(name); try { return await client.get<ProjectDetails>(`/v1/projects/${name}`); } catch (error: unknown) { const err = error as { statusCode?: number; message?: string }; if (err.statusCode === 404) { const { LiaraMcpError } = await import('../utils/errors.js'); throw new LiaraMcpError( `App "${name}" not found`, 'APP_NOT_FOUND', { name }, [ 'Check if the app name is correct', 'Use liara_list_apps to see all available apps', 'Verify you have access to this app' ] ); } throw error; } } /** * Create a new app */ export async function createApp( client: LiaraClient, request: CreateProjectRequest ): Promise<Project> { validateAppName(request.name); validateRequired(request.platform, 'Platform'); validateRequired(request.planID, 'Plan ID'); // Network is optional in schema but will be passed to API if provided // API will return error if network is required but not provided return await client.post<Project>('/v1/projects', request); } /** * Delete an app */ export async function deleteApp( client: LiaraClient, name: string ): Promise<void> { validateAppName(name); await client.delete(`/v1/projects/${name}`); } /** * Start an app (scale up to 1) */ export async function startApp( client: LiaraClient, name: string ): Promise<void> { validateAppName(name); await client.post(`/v1/projects/${name}/actions/scale`, { scale: 1 }); } /** * Stop an app (scale down to 0) */ export async function stopApp( client: LiaraClient, name: string ): Promise<void> { validateAppName(name); await client.post(`/v1/projects/${name}/actions/scale`, { scale: 0 }); } /** * Scale an app to a specific number of replicas */ export async function scaleApp( client: LiaraClient, name: string, replicas: number ): Promise<void> { validateAppName(name); if (replicas < 0) { throw new Error('Replicas must be 0 or greater'); } await client.post(`/v1/projects/${name}/actions/scale`, { scale: replicas }); } /** * Restart an app */ export async function restartApp( client: LiaraClient, name: string ): Promise<void> { validateAppName(name); await client.post(`/v1/projects/${name}/actions/restart`); } /** * Change app plan (resize) */ export async function resizeApp( client: LiaraClient, name: string, planID: string ): Promise<void> { validateAppName(name); validateRequired(planID, 'Plan ID'); await client.post(`/v1/projects/${name}/resize`, { planID }); } /** * Execute a command in the app container * Note: This may require WebSocket support depending on Liara API implementation * For now, we'll attempt to use HTTP endpoint if available */ export async function execCommand( client: LiaraClient, appName: string, command: string, workingDir?: string ): Promise<{ output: string; exitCode: number }> { validateAppName(appName); validateRequired(command, 'Command'); // Try HTTP endpoint first (if available) // Note: Liara may require WebSocket for interactive commands // This is a placeholder implementation try { const response = await client.post<{ output: string; exitCode: number }>( `/v1/projects/${appName}/exec`, { command, workingDir: workingDir || '/app', } ); return response; } catch (error: unknown) { const err = error as { response?: { status?: number }; message?: string }; // If HTTP endpoint doesn't exist, return error with guidance if (err.response?.status === 404) { const { LiaraMcpError } = await import('../utils/errors.js'); throw new LiaraMcpError( 'Command execution requires WebSocket support', 'EXEC_COMMAND_NOT_AVAILABLE', { appName, command }, [ 'Use the Liara CLI: liara app shell --app <name> -- <command>', 'Or implement WebSocket support in the MCP server', 'Check if the app is running and accessible' ] ); } throw error; } } /** * Get available platforms */ export function getAvailablePlatforms(): Platform[] { return [ 'node', 'nextjs', 'laravel', 'php', 'django', 'flask', 'dotnet', 'static', 'react', 'angular', 'vue', 'docker', 'python', 'go', ]; }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/razavioo/liara-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server