From 1ff34408cbb1aa9505eefde6e7cb41909ba4fd9b Mon Sep 17 00:00:00 2001 From: zJ_ <19760191+zJuuu@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:26:00 +0200 Subject: [PATCH] feat: add UpdateDeploymentTool and related query utilities for managing deployments --- README.md | 9 ++-- src/AkashMCP.ts | 9 +++- src/index.ts | 1 - src/tools/create-deployment.ts | 2 +- src/tools/index.ts | 1 + src/tools/send-manifest.ts | 2 +- src/tools/update-deployment.ts | 77 ++++++++++++++++++++++++++++++++++ src/utils/query-lease.ts | 31 ++++++++++++++ src/utils/query-leases.ts | 24 +++++++++++ 9 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 src/tools/update-deployment.ts create mode 100644 src/utils/query-lease.ts create mode 100644 src/utils/query-leases.ts diff --git a/README.md b/README.md index 76d1e9e..96447ce 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -
@@ -10,8 +9,9 @@ **Akash MCP Server** is a TypeScript implementation of a Model Context Protocol (MCP) server for interacting with the Akash Network. [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -[![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/akashnet_)](https://x.com/akashnet_ "Follow Akash Network on X") -[![Discord](https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat)](https://discord.gg/akash "Join Akash Discord") +[![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/akashnet_)](https://x.com/akashnet_ 'Follow Akash Network on X') +[![Discord](https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat)](https://discord.gg/akash 'Join Akash Discord') +
## Note @@ -109,12 +109,13 @@ The server provides the following tools for AI agents: - **GetAccountAddrTool**: Retrieve your Akash account address - **GetBidsTool**: Get bids for deployments -- **CreateDeploymentTool**: Create a new deployment on the Akash Network +- **CreateDeploymentTool**: Create a new deployment on Akash Network - **GetSDLsTool**: Get a list of available SDLs (from awesome-akash repository) - **GetSDLTool**: Get a specific SDL by name - **SendManifestTool**: Send a manifest to a provider - **CreateLeaseTool**: Create a lease with a provider - **GetServicesTool**: Get information about active services +- **UpdateDeploymentTool**: Update a deployment on Akash Network ## Development diff --git a/src/AkashMCP.ts b/src/AkashMCP.ts index 230cf7b..df052ab 100644 --- a/src/AkashMCP.ts +++ b/src/AkashMCP.ts @@ -12,6 +12,7 @@ import { CreateLeaseTool, GetServicesTool, CreateDeploymentTool, + UpdateDeploymentTool, } from './tools/index.js'; import type { ToolContext } from './types/index.js'; import type { CertificatePem } from '@akashnetwork/akashjs/build/certificates/certificate-manager/CertificateManager.js'; @@ -114,8 +115,14 @@ class AkashMCP extends McpServer { GetServicesTool.parameters.shape, async (args, extra) => GetServicesTool.handler(args, this.getToolContext()) ); - } + this.tool( + UpdateDeploymentTool.name, + UpdateDeploymentTool.description, + UpdateDeploymentTool.parameters.shape, + async (args, extra) => UpdateDeploymentTool.handler(args, this.getToolContext()) + ); + } public isInitialized(): boolean { return this.wallet !== null && this.client !== null && this.certificate !== null; } diff --git a/src/index.ts b/src/index.ts index 47ae656..2497fdc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,5 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import AkashMCP from './AkashMCP.js'; -import { GetAccountAddrTool } from './tools/get-account-addr.js'; async function main() { const server = new AkashMCP(); diff --git a/src/tools/create-deployment.ts b/src/tools/create-deployment.ts index af82f12..1aecadb 100644 --- a/src/tools/create-deployment.ts +++ b/src/tools/create-deployment.ts @@ -14,7 +14,7 @@ const parameters = z.object({ export const CreateDeploymentTool: ToolDefinition = { name: 'create-deployment', description: - 'Create a new deployment on Akash Network using the provided SDL (Service Definition Language) string, deposit amount and currency.' + + 'Create a new deployment on Akash Network using the provided SDL (Service Definition Language) string, deposit amount and currency.' + 'The deposit amount is the amount of tokens to deposit into the deployment.' + 'Minimum deposit amount is 500000 uakt.', parameters, diff --git a/src/tools/index.ts b/src/tools/index.ts index ba2dd41..3fe016e 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -6,3 +6,4 @@ export { GetSDLsTool } from './get-sdls.js'; export { GetBidsTool } from './get-bids.js'; export { CreateLeaseTool } from './create-lease.js'; export { GetAccountAddrTool } from './get-account-addr.js'; +export { UpdateDeploymentTool } from './update-deployment.js'; diff --git a/src/tools/send-manifest.ts b/src/tools/send-manifest.ts index a8ff52c..22b2e48 100644 --- a/src/tools/send-manifest.ts +++ b/src/tools/send-manifest.ts @@ -50,7 +50,7 @@ export const SendManifestTool: ToolDefinition = { }, }; -async function sendManifest(sdl: SDL, lease: CustomLease, certificate: CertificatePem) { +export async function sendManifest(sdl: SDL, lease: CustomLease, certificate: CertificatePem) { if (!lease.id) { throw new Error('Lease ID is undefined'); } diff --git a/src/tools/update-deployment.ts b/src/tools/update-deployment.ts new file mode 100644 index 0000000..f048b6f --- /dev/null +++ b/src/tools/update-deployment.ts @@ -0,0 +1,77 @@ +import { z } from 'zod'; +import type { ToolDefinition, ToolContext } from '../types/index.js'; +import { MsgUpdateDeployment } from '@akashnetwork/akash-api/akash/deployment/v1beta3'; +import { SDL } from '@akashnetwork/akashjs/build/sdl/SDL/SDL.js'; +import { createOutput } from '../utils/create-output.js'; +import { getTypeUrl } from '@akashnetwork/akashjs/build/stargate/index.js'; +import { sendManifest } from './send-manifest.js'; +import { queryLeases } from '../utils/query-leases.js'; +const parameters = z.object({ + rawSDL: z.string().min(1), + provider: z.string().min(1), + dseq: z.number().min(1), +}); + +export const UpdateDeploymentTool: ToolDefinition = { + name: 'update-deployment', + description: + 'Update a deployment on Akash Network using the provided SDL (Service Definition Language) string. This tool also sends the manifest to the provider.' + + 'The dseq is the deployment sequence number.' + + 'The provider is the provider of the lease.', + parameters, + handler: async (params: z.infer, context: ToolContext) => { + const { rawSDL, provider } = params; + const { wallet, client, certificate } = context; + + try { + // Parse SDL directly from the string + const sdl = SDL.fromString(rawSDL, 'beta3'); + const accounts = await wallet.getAccounts(); + + if (!accounts || accounts.length === 0) { + return createOutput({ error: 'No accounts found in wallet' }); + } + + const leases = await queryLeases(accounts[0].address, params.dseq, provider); + + if (leases.leases.length === 0) { + return createOutput({ error: 'No leases found for deployment' }); + } + + const lease = leases.leases[0]; + + const msg = { + typeUrl: getTypeUrl(MsgUpdateDeployment), + value: MsgUpdateDeployment.fromPartial({ + id: { + owner: accounts[0].address, + dseq: params.dseq, + }, + version: await sdl.manifestVersion(), + }), + }; + + const tx = await client.signAndBroadcast(accounts[0].address, [msg], 'auto'); + + const leaseId = { + id: { + owner: lease.lease?.leaseId?.owner ?? '', + dseq: lease.lease?.leaseId?.dseq.toNumber() ?? 0, + gseq: lease.lease?.leaseId?.gseq ?? 0, + oseq: lease.lease?.leaseId?.oseq ?? 0, + provider: lease.lease?.leaseId?.provider ?? '', + }, + }; + + // Send manifest to provider + await sendManifest(sdl, leaseId, certificate); + + return createOutput(tx.rawLog); + } catch (error: any) { + console.error('Error updating deployment:', error); + return createOutput({ + error: error.message || 'Unknown error updating deployment', + }); + } + }, +}; diff --git a/src/utils/query-lease.ts b/src/utils/query-lease.ts new file mode 100644 index 0000000..94b0a4f --- /dev/null +++ b/src/utils/query-lease.ts @@ -0,0 +1,31 @@ +import { + QueryLeaseRequest, + QueryClientImpl as QueryMarketClient, +} from '@akashnetwork/akash-api/akash/market/v1beta4'; +import { getRpc } from '@akashnetwork/akashjs/build/rpc/index.js'; +import { SERVER_CONFIG } from '../config.js'; + +export async function queryLease( + owner: string, + dseq: number, + gseq: number, + oseq: number, + provider: string +) { + const rpc = await getRpc(SERVER_CONFIG.rpcEndpoint); + const marketClient = new QueryMarketClient(rpc); + + const request = QueryLeaseRequest.fromPartial({ + id: { + owner, + dseq, + gseq, + oseq, + provider, + }, + }); + + const lease = await marketClient.Lease(request); + + return lease; +} diff --git a/src/utils/query-leases.ts b/src/utils/query-leases.ts new file mode 100644 index 0000000..bcbac3d --- /dev/null +++ b/src/utils/query-leases.ts @@ -0,0 +1,24 @@ +import { + QueryLeaseRequest, + QueryLeasesRequest, + QueryClientImpl as QueryMarketClient, +} from '@akashnetwork/akash-api/akash/market/v1beta4'; +import { getRpc } from '@akashnetwork/akashjs/build/rpc/index.js'; +import { SERVER_CONFIG } from '../config.js'; + +export async function queryLeases(owner: string, dseq: number, provider: string) { + const rpc = await getRpc(SERVER_CONFIG.rpcEndpoint); + const marketClient = new QueryMarketClient(rpc); + + const request = QueryLeasesRequest.fromPartial({ + filters: { + owner, + dseq, + provider, + }, + }); + + const leases = await marketClient.Leases(request); + + return leases; +}