Skip to main content
Glama
Heelc

vmysql-mcp

by Heelc

mysql_exec

Execute a single write-capable SQL statement on a MySQL database environment with policy-based security controls and environment routing.

Instructions

Run one write-capable SQL statement on a configured MySQL environment if policy allows.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
envYesEnvironment alias such as dev or stg.
databaseNoOptional database override allowed by server policy.
sqlYesOne SQL statement allowed by server policy.
timeoutMsNoRequested execution timeout in milliseconds.

Implementation Reference

  • The main handler function for mysql_exec tool. It resolves the execution context using policy, executes the SQL via the pool registry, and builds the success/error result with affected rows, insert ID, warnings, and elapsed time.
    async (args) => {
      try {
        const context = resolveExecContext(registry, args);
        const startedAt = Date.now();
        const result = await pools.exec(
          context.env,
          context.normalizedSql,
          context.database,
          context.timeoutMs,
        );
        const structuredContent: ExecStructuredContent = {
          ok: true,
          env: context.env,
          database: context.database,
          statementType: context.statementType,
          affectedRows: result.affectedRows,
          insertId: result.insertId,
          warnings: result.warnings,
          elapsedMs: Date.now() - startedAt,
        };
    
        return buildSuccessResult(
          `Affected ${structuredContent.affectedRows} row(s) in ${context.env}${context.database ? `.${context.database}` : ""} in ${structuredContent.elapsedMs}ms.`,
          structuredContent,
        );
      } catch (error) {
        return buildErrorResult(error, args.env);
      }
    },
  • Input schema for mysql_exec tool using Zod. Defines validation for env (environment alias), database (optional override), sql (the statement to execute), and timeoutMs (optional execution timeout).
    export const mysqlExecInputSchema = z.object({
      env: z.string().min(1).describe("Environment alias such as dev or stg."),
      database: z
        .string()
        .min(1)
        .optional()
        .describe("Optional database override allowed by server policy."),
      sql: z.string().min(1).describe("One SQL statement allowed by server policy."),
      timeoutMs: z
        .number()
        .int()
        .min(100)
        .optional()
        .describe("Requested execution timeout in milliseconds."),
    });
  • Registration function for mysql_exec tool. Registers the tool with MCP server including title, description, annotations (destructiveHint: true, readOnlyHint: false), input/output schemas, and the handler function.
    export const registerMysqlExecTool = (
      server: McpServer,
      registry: EnvironmentRegistry,
      pools: MysqlPoolRegistry,
    ) => {
      server.registerTool(
        "mysql_exec",
        {
          title: "MySQL Exec",
          description: "Run one write-capable SQL statement on a configured MySQL environment if policy allows.",
          annotations: {
            title: "MySQL Exec",
            readOnlyHint: false,
            destructiveHint: true,
            idempotentHint: false,
            openWorldHint: false,
          },
          inputSchema: mysqlExecInputSchema,
          outputSchema: buildMysqlExecOutputSchema(),
        },
        async (args) => {
          try {
            const context = resolveExecContext(registry, args);
            const startedAt = Date.now();
            const result = await pools.exec(
              context.env,
              context.normalizedSql,
              context.database,
              context.timeoutMs,
            );
            const structuredContent: ExecStructuredContent = {
              ok: true,
              env: context.env,
              database: context.database,
              statementType: context.statementType,
              affectedRows: result.affectedRows,
              insertId: result.insertId,
              warnings: result.warnings,
              elapsedMs: Date.now() - startedAt,
            };
    
            return buildSuccessResult(
              `Affected ${structuredContent.affectedRows} row(s) in ${context.env}${context.database ? `.${context.database}` : ""} in ${structuredContent.elapsedMs}ms.`,
              structuredContent,
            );
          } catch (error) {
            return buildErrorResult(error, args.env);
          }
        },
      );
    };
  • resolveExecContext helper function that validates SQL statements for mysql_exec. Checks for single statement, disallows WITH clauses, verifies write permissions, validates statement types (insert/update/delete/replace/ddl), checks DDL permissions, and resolves timeout settings.
    export const resolveExecContext = (
      registry: EnvironmentRegistry,
      args: ExecArgs,
    ): ResolvedExecutionContext & {
      normalizedSql: string;
      statementType: "insert" | "update" | "delete" | "replace" | "ddl";
    } => {
      const environment = registry.getEnvironment(args.env);
      const inspection = inspectSql(args.sql);
    
      if (!inspection.normalizedSql) {
        throw createError("SQL_EMPTY", "SQL cannot be empty.", false);
      }
      if (inspection.hasMultipleStatements) {
        throw createError(
          "SQL_MULTI_STATEMENT",
          "Only one SQL statement is allowed per call.",
          false,
        );
      }
    
      if (inspection.hasLeadingWithClause) {
        throw createError(
          "SQL_NOT_ALLOWED",
          "Top-level WITH statements are not allowed in mysql_exec.",
          false,
        );
      }
    
      if (environment.readOnly || !environment.allowWrite) {
        throw createError(
          "WRITE_DISABLED",
          `Write statements are not allowed for environment '${args.env}'.`,
          false,
        );
      }
    
      if (!isWriteType(inspection.statementType) && inspection.statementType !== "ddl") {
        throw createError(
          "SQL_NOT_ALLOWED",
          `Statement type '${inspection.mainKeyword || inspection.firstKeyword || "unknown"}' is not allowed in mysql_exec.`,
          false,
        );
      }
    
      if (inspection.statementType === "ddl" && !environment.allowDDL) {
        throw createError(
          "DDL_DISABLED",
          `DDL statements are not allowed for environment '${args.env}'.`,
          false,
        );
      }
    
      const database = args.database ?? environment.defaultDatabase;
      assertAllowedDatabase(environment, database);
      assertReferencedDatabasesAllowed(environment, inspection.normalizedSql);
    
      const serverConfig = registry.getServerConfig();
      return {
        env: args.env,
        database,
        environment,
        timeoutMs: resolveTimeout(
          args.timeoutMs,
          environment.defaultTimeoutMs,
          serverConfig.hardTimeoutMs,
          serverConfig.defaultTimeoutMs,
        ),
        normalizedSql: inspection.normalizedSql,
        statementType: inspection.statementType,
      };
    };
  • The exec method in MysqlPoolRegistry that performs the actual MySQL execution. Gets a connection from the pool, optionally changes to the specified database, executes the SQL with timeout, and returns affectedRows, insertId, and warnings.
    public async exec(
      env: string,
      sql: string,
      database: string | undefined,
      timeoutMs: number,
    ): Promise<ExecExecutionResult> {
      const pool = this.getPool(env);
      const connection = await pool.getConnection();
      try {
        if (database) {
          await connection.changeUser({ database });
        }
        const [result] = await connection.query<ResultSetHeader>({
          sql,
          timeout: timeoutMs,
        });
        return {
          affectedRows: result.affectedRows,
          insertId: result.insertId === 0 ? undefined : result.insertId,
          warnings: result.warningStatus,
        };
      } catch (error) {
        throw this.mapMysqlError(error);
      } finally {
        connection.release();
      }
    }
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/Heelc/vmysql-mcp'

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