Build Your Own MCP: Tool Definitions API

View as Markdown

Build Your Own MCP: Tool Definitions API

Contentstack publishes the same tool definitions this server uses at public, unauthenticated HTTPS endpoints. If you are building your own MCP server, agent framework, or API client, you can fetch these definitions directly and translate a tool call into a Contentstack REST/GraphQL request yourself, no need to vendor this repository.

This document covers:

These endpoints serve tool metadata only: names, descriptions, input schemas, and request mappings. They contain no credentials and perform no calls against your stack. You supply your own tokens when you execute the resulting request.

Tool Definition Endpoints

Each API group is published at https://mcp.contentstack.com/<group>/tools:

GroupEndpoint
cmahttps://mcp.contentstack.com/cma/tools
cma-extendedhttps://mcp.contentstack.com/cma-extended/tools
cdahttps://mcp.contentstack.com/cda/tools
brandkithttps://mcp.contentstack.com/brandkit/tools
lyticshttps://mcp.contentstack.com/lytics/tools
personalizehttps://mcp.contentstack.com/personalize/tools
analyticshttps://mcp.contentstack.com/analytics/tools
launchhttps://mcp.contentstack.com/launch/tools
developerhubhttps://mcp.contentstack.com/developerhub/tools

Each endpoint returns a JSON object keyed by tool name. Example:

curl -s https://mcp.contentstack.com/cda/tools | jq 'keys'

Tool Definition Format

{

  "<tool_name>": {

    "name": "get_all_assets_cdn",

    "description": "Human/LLM-facing description of what the tool does.",

    "group": "cda",

    "subGroup": "ai",   // optional; only some groups (e.g., brandkit) use this field

    "mapper": {

      /* how to build the HTTP request — see Building a Request */

    },

    "inputSchema": {

      /* JSON Schema for the tool's arguments */

    }

  }

}
  • inputSchema is a standard JSON Schema (type, properties, required, default, etc.). Expose it directly to your LLM or tool runner as the tool's parameters.
  • mapper tells you how to translate the validated arguments into an HTTP request.

The mapper object

FieldTypeDescription
apiUrlstringPath appended to the group's base URL. May contain path-parameter placeholders.
methodGET | POST | PUT | DELETEHTTP method.
typestring (optional)"complex" (schema-shaped body) or "graphql". Absent or "object" = simple.
paramsRecord<string, string>Maps a placeholder in apiUrl to the argument name that fills it.
queryParamsRecord<string, string>Maps a query string key to the argument name that provides its value.
headersRecord<string, string>Maps an HTTP header name to the argument name that provides its value.
bodystring | objectFor simple tools: the argument name holding the request body. For complex tools: a sub-schema.

GraphQL tools (type: "graphql") additionally include:

FieldTypeDescription
querystringThe GraphQL query string.
variablesobjectMaps each GraphQL variable to { "type": ..., "x-mapFrom": "<argName>" }.

Building a Request

Given a tool definition and a map of validated arguments, build the HTTP request as follows.

1. Resolve the URL and Path Parameters

Start from mapper.apiUrl. For each entry in mapper.params (placeholder → argName), replace every occurrence of the placeholder in the path with encodeURIComponent(args[argName]). A missing required path parameter is an error.

apiUrl:  /v3/content_types/content_type_uid/entries

params:  { "content_type_uid": "content_type_uid" }

args:    { "content_type_uid": "blog_post" }

=>       /v3/content_types/blog_post/entries

2. Build Query Parameters

For each entry in mapper.queryParams (queryKey → argName), if args[argName] is defined, add it to the query string.

  • If queryKey ends with [], the value is serialized as a repeated array parameter: key[]=a&key[]=b.
  • Object values are JSON-stringified.

3. Build Headers

For each entry in mapper.headers (headerName → argName), if args[argName] is defined, set that header. Then add the authentication headers for the group (see Authentication).

4. Build the Body

  • Simple (body is a string, no type: "complex"): if args[body] exists, the body is exactly that value. Otherwise, the body is built by wrapping all arguments not already consumed by params or queryParams under the body key: { [body]: { ...leftoverArgs } }.
  • Complex (type: "complex", body is a schema): walk the schema; for each leaf carrying x-mapFrom, pull the value from args[x-mapFrom]. Arrays wrap single values into a one-element array. This reconstructs a nested payload from flat arguments.
  • GraphQL (type: "graphql"): POST { query, variables } to apiUrl, where each variable is filled from args[variables[var]["x-mapFrom"]].

5. Assemble

<base URL for group + region>  +  <resolved apiUrl>  +  ?<query string>

Apply the method, headers, and body as constructed in the previous steps.

Base URLs

The base URL is the region endpoint for the service plus an optional path suffix. Region endpoints are available at:

https://artifacts.contentstack.com/regions.json

Map each group to a field in that document as follows:

Groupregions.json fieldPath suffix
cma, cma-extendedcontentManagement
cdacontentDelivery
developerhubdeveloperHub
personalizepersonalizeManagement
analyticsapplication/analytics
launchlaunch/manage
brandkit (subGroup: brand-kits-api)brandKit
brandkit (subGroup: ai)genAI with trailing /brand-kits removed

For example, for the NA region, cma resolves to https://api.contentstack.io and cda to https://cdn.contentstack.io.

Lytics is a third-party service and is not region-specific: https://api.lytics.io.

Authentication

Add headers based on the group. You supply the token values — the tool definitions never include credentials.

GroupRequired headers
cdaapi_key: <stack api key>, access_token: <delivery token>
cma, cma-extended (token auth)api_key: <stack api key>, authorization: <management token>
cma, cma-extended, developerhub, brandkit, analytics, launch, personalize (OAuth)api_key (where applicable) + OAuth bearer headers from your OAuth flow
lyticsAuthorization: <lytics access token>

Group-specific extra headers and parameters used by this server:

  • BrandKit: brand_kit_uid: <brand kit uid>
  • Personalize: x-project-uid: <personalize project id>
  • Launch: x-project-uid, x-organization-uid, plus Apollo client headers
  • Analytics: an orgUid query parameter derived from the OAuth organization

For OAuth-backed groups, you must run Contentstack's OAuth flow yourself and attach the resulting bearer token. cma and cma-extended can avoid OAuth entirely by using a management token as shown in the table above. See the Contentstack MCP Server user guide for details.

Worked Examples

Example 1: CDA: List Assets (GET, query parameters and header)

Definition:

{

  "name": "get_all_assets_cdn",

  "group": "cda",

  "mapper": {

    "apiUrl": "/v3/assets",

    "method": "GET",

    "queryParams": {

      "limit": "limit",

      "skip": "skip",

      "include_count": "include_count"

    },

    "headers": { "branch": "branch" }

  }

}

Call with args = { limit: 10, include_count: true, branch: "main" }, region EU:

GET https://eu-cdn.contentstack.com/v3/assets?limit=10&include_count=true

api_key: <stack api key>

access_token: <delivery token>

branch: main

Example 2: CMA: Create an Entry (POST, path parameter and body wrapper)

Definition:

{

  "name": "create_an_entry",

  "group": "cma",

  "mapper": {

    "apiUrl": "/v3/content_types/content_type_uid/entries",

    "method": "POST",

    "body": "entry_data",

    "params": { "content_type_uid": "content_type_uid" },

    "queryParams": { "locale": "locale" },

    "headers": { "branch": "branch" }

  }

}

Call with args = { content_type_uid: "blog_post", locale: "en-us", entry_data: { entry: { title: "Hello" } }, branch: "main" }, region NA, management-token auth:

POST https://api.contentstack.io/v3/content_types/blog_post/entries?locale=en-us

api_key: <stack api key>

authorization: <management token>

branch: main

Content-Type: application/json


{ "entry": { "title": "Hello" } }

Here entry_data is named by mapper.body, so its value is used directly as the request body.

Example 3: Launch: GraphQL (type: "graphql")

Definition:

{

  "name": "get_environments",

  "group": "launch",

  "mapper": {

    "type": "graphql",

    "method": "POST",

    "apiUrl": "/graphql",

    "query": "query Environment($first: Float) { Environments(first: $first) { edges { node { name uid } } } }",

    "variables": { "first": { "type": "Float", "x-mapFrom": "first" } }

  }

}

Call with args = { first: 10 }, region NA:

POST https://launch-api.contentstack.com/manage/graphql

x-project-uid: <launch project id>

x-organization-uid: <organization uid>

<OAuth bearer headers>

Content-Type: application/json


{ "query": "query Environment($first: Float) { ... }", "variables": { "first": 10 } }