Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ MCP сервер для автоматизации деплоя приложен

Получает настройки деплоя по умолчанию для различных фреймворков.

### `create_floating_ip`

Создает новый floating IP адрес в указанной зоне доступности.

### `create_vpc`

Создает новую виртуальную приватную сеть (VPC) в указанной зоне доступности.

### `create_database`

Создает новую базу данных в Timeweb Cloud с указанными параметрами.

### `get_database_presets`

Получает список доступных пресетов конфигураций для создания баз данных.

## Промпты

### `create_app_prompt`
Expand Down
9 changes: 4 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/actions/create-database.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { dbaasApiClient } from "../api";
import { Database } from "../types/database.type";
import { CreateDbParams } from "../types/create-db-params.type";

export const createDatabaseAction = async (params: CreateDbParams): Promise<Database> => {
return await dbaasApiClient.createDatabase(params);
};
10 changes: 10 additions & 0 deletions src/actions/create-floating-ip.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { dbaasApiClient } from "../api";
import { FloatingIp } from "../types/floating-ip.type";
import { AvailabilityZones } from "../types/availability-zones.enum";

export const createFloatingIpAction = async (
availabilityZone: AvailabilityZones,
isDdosGuard: boolean = false
): Promise<FloatingIp> => {
return await dbaasApiClient.createFloatingIp(availabilityZone, isDdosGuard);
};
11 changes: 11 additions & 0 deletions src/actions/create-vpc.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { dbaasApiClient } from "../api";
import { AvailabilityZones } from "../types/availability-zones.enum";
import { Vpc } from "../types/vpc.type";

export const createVpcAction = async (
availabilityZone: AvailabilityZones,
name: string,
subnetV4: string
): Promise<Vpc> => {
return await dbaasApiClient.createVpc(availabilityZone, name, subnetV4);
};
7 changes: 7 additions & 0 deletions src/actions/get-database-presets.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { dbaasApiClient } from "../api";
import { DatabasePreset } from "../types/database-preset.type";

export const getDatabasePresetsAction =
async (): Promise<DatabasePreset[]> => {
return await dbaasApiClient.getDatabasePresets();
};
7 changes: 7 additions & 0 deletions src/actions/get-vpcs.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { dbaasApiClient } from "../api";

export const getVpcsAction = async () => {
const response = await dbaasApiClient.getVpcs();

return response.vpcs;
};
120 changes: 120 additions & 0 deletions src/api/dbaas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { BaseApiClient } from "./client";
import { CreateFloatingIpRequestDto } from "../types/dto/create-floating-ip-request.dto";
import { CreateFloatingIpResponseDto } from "../types/dto/create-floating-ip-response.dto";
import { CreateVpcRequestDto } from "../types/dto/create-vpc-request.dto";
import { CreateVpcResponseDto } from "../types/dto/create-vpc-response.dto";
import { CreateDatabaseRequestDto } from "../types/dto/create-database-request.dto";
import { CreateDatabaseResponseDto } from "../types/dto/create-database-response.dto";
import { Database } from "../types/database.type";
import {
DatabasePreset,
DatabasePresetsResponse,
} from "../types/database-preset.type";
import { AvailabilityZones } from "../types/availability-zones.enum";
import { CreateDbParams } from "../types/create-db-params.type";
import { Vpc } from "../types/vpc.type";
import { FloatingIp } from "../types/floating-ip.type";
import { GetVpcsResponseDto } from "../types/dto/get-vpcs-response.dto";

export class DbaasApiClient extends BaseApiClient {
/**
* Создает новый floating IP адрес
*/
async createFloatingIp(
availabilityZone: AvailabilityZones,
isDdosGuard: boolean = false
): Promise<FloatingIp> {
const requestData: CreateFloatingIpRequestDto = {
availability_zone: availabilityZone,
is_ddos_guard: isDdosGuard,
};

const response = await this.post<CreateFloatingIpResponseDto>(
"/api/v1/floating-ips",
requestData
);
return response.ip;
}

/**
* Получить список VPC пользователя
*/
async getVpcs(): Promise<GetVpcsResponseDto> {
return this.get<GetVpcsResponseDto>("/api/v2/vpcs");
}

/**
* Создает новую виртуальную приватную сеть (VPC)
*/
async createVpc(
availabilityZone: AvailabilityZones,
name: string,
subnetV4: string
): Promise<Vpc> {
const requestData: CreateVpcRequestDto = {
availability_zone: availabilityZone,
name: name,
subnet_v4: subnetV4,
};

const response = await this.post<CreateVpcResponseDto>(
"/api/v2/vpcs",
requestData
);
return response.vpc;
}

/**
* Создает новую базу данных
*/
async createDatabase({
name,
type,
presetId,
availabilityZone,
adminPassword,
floatingIp,
vpcId,
hashType,
}: CreateDbParams): Promise<Database> {
const requestData: CreateDatabaseRequestDto = {
admin: {
password: adminPassword,
for_all: false,
},
name: name,
type: type,
preset_id: presetId,
availability_zone: availabilityZone,
hash_type: hashType,
auto_backups: {
copy_count: 1,
creation_start_at: new Date().toISOString(),
interval: "day",
day_of_week: 5,
},
network: {
floating_ip: floatingIp,
id: vpcId,
},
};

const response = await this.post<CreateDatabaseResponseDto>(
"/api/v1/databases",
requestData
);
return response.db;
}

/**
* Получает список пресетов баз данных
*/
async getDatabasePresets(): Promise<DatabasePreset[]> {
const response = await this.get<DatabasePresetsResponse>(
"/api/v2/presets/dbs"
);
return response.databases_presets;
}
}

export const dbaasApiClient: DbaasApiClient = new DbaasApiClient();
1 change: 1 addition & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { appsApiClient } from "./apps";
export { dbaasApiClient } from "./dbaas";
47 changes: 47 additions & 0 deletions src/resources/database-presets.resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { getDatabasePresetsAction } from "../actions/get-database-presets.action";
import { ResourceNames } from "../types/resource-names.enum";

export const databasePresetsResource = {
name: ResourceNames.DATABASE_PRESETS,
title: "Пресеты баз данных",
description: "Список доступных пресетов конфигураций для создания баз данных",
mimeType: "application/json",
handler: async () => {
try {
const presets = await getDatabasePresetsAction();

if (!presets || !presets.length) {
return {
contents: [
{
type: "text" as const,
text: "❌ Не удалось получить список пресетов баз данных",
},
],
};
}

const content = `📊 **Пресеты баз данных Timeweb Cloud**\n\n;${JSON.stringify(presets, null, 2)}`;

return {
contents: [
{
type: "text" as const,
text: content,
},
],
};
} catch (error) {
return {
contents: [
{
type: "text" as const,
text: `❌ Ошибка получения пресетов баз данных: ${
error instanceof Error ? error.message : "Неизвестная ошибка"
}`,
},
],
};
}
},
};
34 changes: 34 additions & 0 deletions src/resources/get-vpcs.resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { ResourceNames } from "../types/resource-names.enum";
import { createResourceResponse } from "../utils";
import { getVpcsAction } from "../actions/get-vpcs.action";
import { ToolNames } from "../types/tool-names.enum";

export const getVpcsResource = {
name: ResourceNames.GET_VPCS,
uri: "vpc://all",
template: new ResourceTemplate("vpc://all", { list: undefined }),
title: "Список VPC пользователя",
description:
"Список виртуальных частных сетей (VPC) пользователя в Timeweb Cloud",
handler: async (uri: URL) => {
try {
const result = await getVpcsAction();

if (!result || result.length === 0) {
return createResourceResponse(uri.href, `Нет доступных VPC. Создайте первую VPC с помощью tool ${ToolNames.CREATE_VPC}`);
}

return createResourceResponse(uri.href, JSON.stringify(result, null, 2));
} catch (error: unknown) {
if (error instanceof Error) {
return createResourceResponse(
uri.href,
`Не удалось получить список VPC. Причина: ${error.message}`
);
}

return createResourceResponse(uri.href, `Не удалось получить список VPC`);
}
},
};
2 changes: 2 additions & 0 deletions src/resources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export { vcsProvidersResource } from "./vcs-providers.resource";
export { vcsProviderRepositoriesResource } from "./vcs-provider-repositories.resource";
export { allowedPresetsResource } from "./allowed-presets.resource";
export { deploySettingsResource } from "./deploy-settings.resource";
export { databasePresetsResource } from "./database-presets.resource";
export { getVpcsResource } from "./get-vpcs.resource";
Loading