Skip to main content
Glama
ngh1105
by ngh1105

genlayer_deploy

Deploy a GenLayer Intelligent Contract with automatic private key handling. Accepts contract path and optional RPC, chain, and argument parameters.

Instructions

Deploy a GenLayer Intelligent Contract with genlayer-js. Accepts privateKey; generates one when missing or invalid.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
contractPathYesPath to the Intelligent Contract source file.
privateKeyNoGenLayer private key. If missing or invalid, a new private key is generated with genlayer-js.
argsNoConstructor arguments passed to deployContract.
kwargsNoKeyword constructor arguments passed to deployContract.
rpcUrlNoCustom GenLayer RPC endpoint.
chainNoGenLayer chain used when creating the SDK client.localnet
cwdNoBase directory for resolving contractPath. Defaults to MCP server cwd.
leaderOnlyNo
consensusMaxRotationsNo
initializeConsensusNo
autoFundLocalnetNoCall localnet sim_fundAccount before deploy. Only works on localnet.
fundAmountNo
waitForReceiptNo
receiptStatusNoACCEPTED
receiptRetriesNo
receiptIntervalMsNo
timeoutMsNo
exposePrivateKeyNoReturn the generated/used private key in output. Defaults false to avoid leaking secrets.

Implementation Reference

  • src/index.ts:498-503 (registration)
    Registration of the 'genlayer_deploy' tool with the MCP server using server.tool(). Delegates to safeDeployWithSdk.
    server.tool(
      "genlayer_deploy",
      "Deploy a GenLayer Intelligent Contract with genlayer-js. Accepts privateKey; generates one when missing or invalid.",
      deployInputShape,
      async (input) => textResponse(await safeDeployWithSdk(input))
    );
  • Core handler function 'deployWithSdk' that performs the actual deployment logic: reads the contract file, normalizes/generates a private key, creates a genlayer-js client, optionally funds a localnet account, deploys the contract, optionally waits for a receipt, and returns the result.
    async function deployWithSdk(input: z.infer<typeof deployInputSchema>) {
      const baseCwd = input.cwd ?? processCwd();
      const contractPath = resolve(baseCwd, input.contractPath);
    
      if (!existsSync(contractPath)) {
        throw new Error(`Contract file not found: ${contractPath}`);
      }
    
      const configuredPrivateKey = getConfiguredPrivateKey();
      const selectedPrivateKey = input.privateKey ?? configuredPrivateKey.value;
      const normalizedPrivateKey = normalizePrivateKey(selectedPrivateKey);
      const providedPrivateKeyValid = Boolean(normalizedPrivateKey);
      const generatedPrivateKey = !providedPrivateKeyValid;
      const privateKey = (normalizedPrivateKey ?? generatePrivateKey()) as `0x${string}`;
      const account = createAccount(privateKey);
      const contractCode = readFileSync(contractPath, "utf8");
    
      if (!contractCode.trim()) {
        throw new Error(`Contract file is empty: ${contractPath}`);
      }
    
      const client = createClient({
        chain: chains[input.chain],
        endpoint: input.rpcUrl,
        account
      });
    
      const deployOperation = async () => {
        if (input.initializeConsensus) {
          await client.initializeConsensusSmartContract();
        }
    
        let funded = false;
        let fundingError: string | undefined;
        if (input.autoFundLocalnet && input.chain === "localnet") {
          try {
            await (client as unknown as { fundAccount(args: { address: string; amount: number }): Promise<unknown> })
              .fundAccount({
                address: account.address,
                amount: input.fundAmount
              });
            funded = true;
          } catch (error) {
            fundingError = error instanceof Error ? error.message : String(error);
          }
        }
    
        const transactionHash = await client.deployContract({
          account,
          code: contractCode,
          args: input.args as Parameters<typeof client.deployContract>[0]["args"],
          kwargs: input.kwargs as Parameters<typeof client.deployContract>[0]["kwargs"],
          leaderOnly: input.leaderOnly,
          consensusMaxRotations: input.consensusMaxRotations
        });
    
        const receipt = input.waitForReceipt
          ? await client.waitForTransactionReceipt({
              hash: transactionHash as Parameters<typeof client.waitForTransactionReceipt>[0]["hash"],
              status:
                input.receiptStatus === "FINALIZED"
                  ? TransactionStatus.FINALIZED
                  : TransactionStatus.ACCEPTED,
              retries: input.receiptRetries,
              interval: input.receiptIntervalMs
            })
          : undefined;
    
        const contractAddress =
          (receipt as { data?: { contract_address?: unknown }; txDataDecoded?: { contractAddress?: unknown } } | undefined)
            ?.data?.contract_address ??
          (receipt as { txDataDecoded?: { contractAddress?: unknown } } | undefined)?.txDataDecoded
            ?.contractAddress;
    
        return {
          ok: true,
          contractPath,
          chain: input.chain,
          rpcUrl: input.rpcUrl,
          accountAddress: account.address,
          privateKeySource: providedPrivateKeyValid
            ? input.privateKey
              ? "input"
              : configuredPrivateKey.source
            : "generated",
          privateKey: generatedPrivateKey || input.exposePrivateKey ? privateKey : undefined,
          privateKeyExposed: generatedPrivateKey || input.exposePrivateKey,
          deployAttempted: true,
          funded,
          fundingError,
          transactionHash,
          contractAddress,
          receipt
        };
      };
    
      return await withTimeout(deployOperation(), input.timeoutMs);
    }
  • Safe wrapper around deployWithSdk that catches errors and returns them as structured failure responses.
    async function safeDeployWithSdk(input: z.infer<typeof deployInputSchema>) {
      try {
        return await deployWithSdk(input);
      } catch (error) {
        return {
          ok: false,
          error: error instanceof Error ? error.message : String(error)
        };
      }
    }
  • Zod schema definition for genlayer_deploy input parameters, defining all accepted fields with types, defaults, and descriptions.
    const deployInputShape = {
      contractPath: z.string().describe("Path to the Intelligent Contract source file."),
      privateKey: z
        .string()
        .optional()
        .describe("GenLayer private key. If missing or invalid, a new private key is generated with genlayer-js."),
      args: z.array(z.unknown()).default([]).describe("Constructor arguments passed to deployContract."),
      kwargs: z.record(z.unknown()).optional().describe("Keyword constructor arguments passed to deployContract."),
      rpcUrl: z.string().url().optional().describe("Custom GenLayer RPC endpoint."),
      chain: z
        .enum(["localnet", "studionet", "testnetAsimov", "testnetBradbury"])
        .default("localnet")
        .describe("GenLayer chain used when creating the SDK client."),
      cwd: z.string().optional().describe("Base directory for resolving contractPath. Defaults to MCP server cwd."),
      leaderOnly: z.boolean().default(false),
      consensusMaxRotations: z.number().int().positive().optional(),
      initializeConsensus: z.boolean().default(true),
      autoFundLocalnet: z
        .boolean()
        .default(true)
        .describe("Call localnet sim_fundAccount before deploy. Only works on localnet."),
      fundAmount: z.number().positive().default(10),
      waitForReceipt: z.boolean().default(true),
      receiptStatus: z.enum(["ACCEPTED", "FINALIZED"]).default("ACCEPTED"),
      receiptRetries: z.number().int().positive().default(50),
      receiptIntervalMs: z.number().int().positive().default(5000),
      timeoutMs: z.number().int().positive().max(30 * 60 * 1000).default(120_000),
      exposePrivateKey: z
        .boolean()
        .default(false)
        .describe("Return the generated/used private key in output. Defaults false to avoid leaking secrets.")
    };
    
    const deployInputSchema = z.object(deployInputShape);
  • Helper functions: normalizePrivateKey validates and normalizes private key format (0x-prefixed or raw hex); getConfiguredPrivateKey looks up private key from environment variables.
    function normalizePrivateKey(privateKey: string | undefined): `0x${string}` | undefined {
      if (!privateKey) {
        return undefined;
      }
    
      const trimmed = privateKey.trim();
      if (PRIVATE_KEY_RE.test(trimmed)) {
        return trimmed as `0x${string}`;
      }
    
      if (RAW_PRIVATE_KEY_RE.test(trimmed)) {
        return `0x${trimmed}`;
      }
    
      return undefined;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

In the absence of annotations, the description bears full responsibility for disclosing behavioral traits. It mentions the key generation fallback for privateKey but fails to disclose other significant behaviors such as error handling, side effects, or return value structure. This is insufficient for an 18-parameter tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise: two short sentences that front-load the core purpose and immediately add an important behavioral note. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (18 optional parameters, one required), the description is far from complete. It lacks information about error handling, output structure, behavior of optional parameters, or any post-deployment steps. An output schema would help, but it is absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 50%, so the baseline is 3. The description adds value by explaining the privateKey fallback behavior beyond the schema. However, it does not compensate for the remaining 50% of parameters that lack schema descriptions, resulting in only marginal improvement.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it deploys a GenLayer Intelligent Contract using genlayer-js, which is a specific verb-resource combination. It distinguishes itself from sibling tools like check_tools, genlayer, and genvm_lint, which serve different purposes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives, nor does it mention prerequisites or context for usage. It only describes the action without any contextual usage advice.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/ngh1105/genlayer-cli-mcp'

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