Skip to main content
Glama

strapi_rest

Execute REST API requests against Strapi CMS endpoints to read, create, update, and delete content. Supports content types, components, filtering, and pagination with user authorization required for write operations.

Instructions

Execute REST API requests against Strapi endpoints. IMPORTANT: All write operations (POST, PUT, DELETE) require explicit user authorization via the userAuthorized parameter.

  1. Reading components: params: { populate: ['SEO'] } // Populate a component params: { populate: { SEO: { fields: ['Title', 'seoDescription'] } } } // With field selection

  2. Updating components (REQUIRES USER AUTHORIZATION): body: { data: { // For single components: componentName: { Title: 'value', seoDescription: 'value' }, // For repeatable components: componentName: [ { field: 'value' } ] } } userAuthorized: true // Must set this to true for POST/PUT/DELETE after getting user permission

  3. Other parameters:

  • fields: Select specific fields

  • filters: Filter results

  • sort: Sort results

  • pagination: Page through results

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
serverNoThe name of the server to connect to
endpointNoThe API endpoint (e.g., 'api/articles')
methodNoHTTP method to useGET
paramsNoOptional query parameters for GET requests. For components, use populate: ['componentName'] or populate: { componentName: { fields: ['field1'] } }
bodyNoRequest body for POST/PUT requests. For components, use: { data: { componentName: { field: 'value' } } } for single components or { data: { componentName: [{ field: 'value' }] } } for repeatable components
userAuthorizedNoREQUIRED for POST/PUT/DELETE operations. Client MUST obtain explicit user authorization before setting this to true.

Implementation Reference

  • The core handler function that executes the strapi_rest tool logic: validates authorization for writes, constructs the request URL with params, makes the fetch call to Strapi, logs performance, and handles the response/error.
    async function makeRestRequest(
        serverName: string,
        endpoint: string,
        method: string = 'GET',
        params?: Record<string, any>,
        body?: Record<string, any>,
        userAuthorized: boolean = false,
        requestId?: string
    ): Promise<any> {
        // Check for write operations that require explicit user authorization
        if ((method === 'POST' || method === 'PUT' || method === 'DELETE') && !userAuthorized) {
            throw new McpError(
                ErrorCode.InvalidParams,
                `AUTHORIZATION REQUIRED: ${method} operations require explicit user authorization.\n\n` +
                `IMPORTANT: The client MUST:\n` +
                `1. Ask the user for explicit permission before making this request\n` +
                `2. Show the user exactly what data will be modified\n` +
                `3. Receive clear confirmation from the user\n` +
                `4. Set userAuthorized=true when making the request\n\n` +
                `This is a security measure to prevent unauthorized data modifications.`
            );
        }
    
        const serverConfig = getServerConfig(serverName);
        let url = `${serverConfig.API_URL}/${endpoint}`;
    
        // Parse query parameters if provided
        if (params) {
            const queryString = qs.stringify(params, {
                encodeValuesOnly: true
            });
            if (queryString) {
                url = `${url}?${queryString}`;
            }
        }
    
        const headers = {
            'Authorization': `Bearer ${serverConfig.JWT}`,
            'Content-Type': 'application/json',
        };
    
        const requestOptions: RequestInit = {
            method,
            headers,
        };
    
        if (body && (method === 'POST' || method === 'PUT')) {
            requestOptions.body = JSON.stringify(body);
        }
    
        const startTime = Date.now();
        
        logger.debug(`Making REST request to Strapi`, {
            requestId,
            server: serverName,
            endpoint,
            method,
            hasParams: !!params,
            hasBody: !!body,
            userAuthorized,
            url: url.replace(serverConfig.JWT, '[REDACTED]')
        });
    
        try {
            const response = await fetch(url, requestOptions);
            const duration = Date.now() - startTime;
            
            logger.logApiCall(
                requestId || 'unknown',
                method,
                endpoint,
                duration,
                response.status,
                serverName
            );
            
            return await handleStrapiError(response, `REST request to ${endpoint}`, requestId);
        } catch (error) {
            const duration = Date.now() - startTime;
            
            logger.error(`REST request to ${endpoint} failed`, {
                requestId,
                server: serverName,
                endpoint,
                method,
                duration,
                errorType: error instanceof Error ? error.constructor.name : typeof error
            }, error instanceof Error ? error : undefined);
            
            throw error;
        }
    }
  • Zod schema defining input validation for the strapi_rest tool, including server, endpoint, method, params, body, and userAuthorized flag with refinement for write ops.
    const RestSchema = z.object({
        server: z.string().min(1, "Server name is required and cannot be empty"),
        endpoint: z.string().min(1, "Endpoint is required and cannot be empty"),
        method: z.enum(["GET", "POST", "PUT", "DELETE"], {
            errorMap: () => ({ message: "Method must be one of: GET, POST, PUT, DELETE" })
        }).optional().default("GET"),
        params: z.union([
            z.record(z.any()),
            z.string().transform((str, ctx) => {
                try {
                    return JSON.parse(str);
                } catch (e) {
                    ctx.addIssue({
                        code: z.ZodIssueCode.custom,
                        message: "Params must be a valid JSON object or object"
                    });
                    return z.NEVER;
                }
            })
        ]).optional(),
        body: z.union([
            z.record(z.any()),
            z.string().transform((str, ctx) => {
                try {
                    return JSON.parse(str);
                } catch (e) {
                    ctx.addIssue({
                        code: z.ZodIssueCode.custom,
                        message: "Body must be a valid JSON object or object"
                    });
                    return z.NEVER;
                }
            })
        ]).optional(),
        userAuthorized: z.union([
            z.boolean(),
            z.string().transform((str, ctx) => {
                if (str === "true") return true;
                if (str === "false") return false;
                ctx.addIssue({
                    code: z.ZodIssueCode.custom,
                    message: "userAuthorized must be boolean true/false or string 'true'/'false'"
                });
                return z.NEVER;
            })
        ]).optional().default(false)
    }).strict().refine(
        (data) => {
            // For write operations, ensure userAuthorized is explicitly set to true
            if (["POST", "PUT", "DELETE"].includes(data.method) && !data.userAuthorized) {
                return false;
            }
            return true;
        },
        {
            message: "Write operations (POST, PUT, DELETE) require explicit user authorization (userAuthorized: true)",
            path: ["userAuthorized"]
        }
    );
  • src/index.ts:1288-1344 (registration)
    Tool registration in the ListTools handler: defines name, detailed description with examples, and inputSchema derived from Zod schema.
    name: "strapi_rest",
    description: "Execute REST API requests against Strapi endpoints. IMPORTANT: All write operations (POST, PUT, DELETE) require explicit user authorization via the userAuthorized parameter.\n\n" +
        "1. Reading components:\n" +
        "params: { populate: ['SEO'] } // Populate a component\n" +
        "params: { populate: { SEO: { fields: ['Title', 'seoDescription'] } } } // With field selection\n\n" +
        "2. Updating components (REQUIRES USER AUTHORIZATION):\n" +
        "body: {\n" +
        "  data: {\n" +
        "    // For single components:\n" +
        "    componentName: {\n" +
        "      Title: 'value',\n" +
        "      seoDescription: 'value'\n" +
        "    },\n" +
        "    // For repeatable components:\n" +
        "    componentName: [\n" +
        "      { field: 'value' }\n" +
        "    ]\n" +
        "  }\n" +
        "}\n" +
        "userAuthorized: true // Must set this to true for POST/PUT/DELETE after getting user permission\n\n" +
        "3. Other parameters:\n" +
        "- fields: Select specific fields\n" +
        "- filters: Filter results\n" +
        "- sort: Sort results\n" +
        "- pagination: Page through results",
    inputSchema: {
        ...zodToJsonSchema(ToolSchemas.strapi_rest),
        properties: {
            ...zodToJsonSchema(ToolSchemas.strapi_rest).properties,
            server: {
                ...zodToJsonSchema(ToolSchemas.strapi_rest).properties.server,
                description: "The name of the server to connect to"
            },
            endpoint: {
                ...zodToJsonSchema(ToolSchemas.strapi_rest).properties.endpoint,
                description: "The API endpoint (e.g., 'api/articles')"
            },
            method: {
                ...zodToJsonSchema(ToolSchemas.strapi_rest).properties.method,
                description: "HTTP method to use",
                default: "GET"
            },
            params: {
                ...zodToJsonSchema(ToolSchemas.strapi_rest).properties.params,
                description: "Optional query parameters for GET requests. For components, use populate: ['componentName'] or populate: { componentName: { fields: ['field1'] } }"
            },
            body: {
                ...zodToJsonSchema(ToolSchemas.strapi_rest).properties.body,
                description: "Request body for POST/PUT requests. For components, use: { data: { componentName: { field: 'value' } } } for single components or { data: { componentName: [{ field: 'value' }] } } for repeatable components"
            },
            userAuthorized: {
                ...zodToJsonSchema(ToolSchemas.strapi_rest).properties.userAuthorized,
                description: "REQUIRED for POST/PUT/DELETE operations. Client MUST obtain explicit user authorization before setting this to true.",
                default: false
            }
        }
    },
  • Dispatch handler in CallToolRequestSchema: validates input, extracts params, calls the makeRestRequest handler, and formats response.
    // Validate input using Zod (includes authorization check)
    const validatedArgs = validateToolInput("strapi_rest", args, requestId);
    const { server, endpoint, method, params, body, userAuthorized } = validatedArgs;
    logger.startRequest(requestId, name, server);
    
    const data = await makeRestRequest(server, endpoint, method, params, body, userAuthorized, requestId);
    result = {
        content: [
            {
                type: "text",
                text: JSON.stringify(data, null, 2),
            },
        ],
    };
  • Helper function used by the handler to process Strapi responses, extract errors with hints, log, and throw McpError if failed.
    async function handleStrapiError(response: Response, context: string, requestId?: string): Promise<any> {
        if (!response.ok) {
            let errorMessage = `${context} failed with status: ${response.status}`;
            let errorData: any = null;
            
            try {
                errorData = await response.json() as any;
                if (errorData && typeof errorData === 'object' && 'error' in errorData) {
                    errorMessage += ` - ${errorData.error?.message || JSON.stringify(errorData.error)}`;
    
                    // Add helpful hints based on status
                    if (response.status === 400) {
                        errorMessage += "\nHINT: Check the request structure matches Strapi's expectations. For v4/v5 differences, refer to Strapi's migration guide.";
                    } else if (response.status === 404) {
                        errorMessage += "\nHINT: Check the endpoint path and ID are correct.";
                    }
                }
            } catch {
                errorMessage += ` - ${response.statusText}`;
            }
            
            logger.error(`Strapi API error: ${context}`, {
                requestId,
                status: response.status,
                statusText: response.statusText,
                url: response.url,
                errorData: errorData,
                context
            });
            
            throw new McpError(ErrorCode.InternalError, errorMessage);
        }
        
        logger.debug(`Strapi API success: ${context}`, {
            requestId,
            status: response.status,
            url: response.url
        });
        
        return response.json();
    }
Behavior4/5

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

Since no annotations are provided, the description carries the full burden of behavioral disclosure. It effectively describes critical behavioral traits: it specifies that write operations (POST, PUT, DELETE) require explicit user authorization via the userAuthorized parameter, which is a key safety and permission constraint. It also provides usage examples for reading and updating components, adding practical context. However, it doesn't cover other behavioral aspects like rate limits, error handling, or response formats, leaving some gaps.

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

Conciseness3/5

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

The description is appropriately sized and front-loaded with the core purpose and authorization requirement. However, it includes numbered sections and code examples that, while informative, could be more streamlined. Some sentences, like the examples for reading and updating components, are detailed but necessary for clarity, making it moderately concise but with room for improvement in structure.

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

Completeness3/5

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

Given the complexity (6 parameters, no annotations, no output schema), the description is somewhat complete but has gaps. It covers authorization needs and provides examples for component handling, which is valuable. However, it lacks information on output values, error cases, or how to handle other Strapi features beyond components. With no output schema, the description should ideally hint at response formats, but it doesn't, leaving the agent uncertain about what to expect.

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

Parameters4/5

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

The schema description coverage is 100%, so the baseline is 3. The description adds significant value beyond the schema by providing concrete examples for params and body usage with components (e.g., populate structures, data formats for single/repeatable components) and clarifying the userAuthorized requirement for write operations. This enhances understanding of parameter usage, though it doesn't cover all parameters in detail (e.g., server, endpoint, method).

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

Purpose4/5

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

The description clearly states the tool's purpose as 'Execute REST API requests against Strapi endpoints,' which is a specific verb+resource combination. However, it doesn't explicitly differentiate itself from sibling tools like strapi_get_components or strapi_get_content_types, which appear to be more specialized read-only tools. The description covers the general REST capability but doesn't clarify why one would use this over the more specific siblings.

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

Usage Guidelines4/5

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

The description provides clear context on when to use certain features, such as requiring user authorization for write operations and including examples for reading vs. updating components. However, it doesn't explicitly state when to use this tool versus the sibling tools (e.g., strapi_get_components for component-specific reads), nor does it mention exclusions or alternatives. The guidance is strong for operational details but lacks sibling differentiation.

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/misterboe/strapi-mcp-server'

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