Retrieve Custom Data with Medusa MCP

In this guide, you'll learn how to expose custom data from your store to AI tools connected to the Medusa MCP server, using abandoned carts as an example.

Why Add Custom API Routes?#

The Medusa MCP server retrieves data by calling your store's GET API routes. Medusa's Admin and Store API routes cover common requests, such as listing orders or products. However, some questions need data that no existing API route returns, such as:

  • Carts that customers abandoned in the last day.
  • Products that haven't sold in the last month.
  • Data from your own custom modules, such as brands or suppliers.

To answer these questions, create a custom GET API route under the /admin or /store prefix that returns the data. After you deploy the API route to Cloud, the AI tool finds and calls it when you ask a related question.

Note: The AI tool can't find or call custom API routes under other prefixes, such as /custom. It also can't call POST or DELETE API routes, since the Medusa MCP server is read-only.

Choose the API Route Prefix#

The prefix of your API route determines how the AI tool calls it:

  • /admin: The AI tool calls the API route as your admin user. Use this prefix for data that only admin users should access, such as abandoned carts or customer details.
  • /store: The AI tool calls the API route as a guest customer with a publishable API key. Use this prefix for data that shoppers can access, such as products in a sales channel.
Warning: The AI tool can't call /store API routes that require a logged-in customer. If the AI tool needs that data, expose it in an /admin API route instead.

Example: Retrieve Abandoned Carts#

In this section, you'll create an API route that returns abandoned carts. You can then ask your AI tool questions like "Which carts were abandoned in the last 24 hours?".

Step 1: Add the Validation Middleware#

First, define a Zod schema that validates the API route's query parameters, and apply it to the API route in src/api/middlewares.ts:

src/api/middlewares.ts
1import {2  defineMiddlewares,3  validateAndTransformQuery,4} from "@medusajs/framework/http"5import { z } from "@medusajs/framework/zod"6
7export const AdminGetAbandonedCartsParams = z.object({8  /**9   * Return carts not updated in this number of hours.10   */11  hours: z.coerce.number().min(1).default(24),12  /**13   * The maximum number of carts to return.14   */15  limit: z.coerce.number().max(100).default(20),16  /**17   * The number of carts to skip.18   */19  offset: z.coerce.number().default(0),20})21
22export type AdminGetAbandonedCartsParamsType = z.infer<23  typeof AdminGetAbandonedCartsParams24>25
26export default defineMiddlewares({27  routes: [28    {29      matcher: "/admin/abandoned-carts",30      methods: ["GET"],31      middlewares: [32        validateAndTransformQuery(33          AdminGetAbandonedCartsParams,34          {}35        ),36      ],37    },38  ],39})

The schema accepts the following query parameters:

  • hours: Carts that weren't updated within this number of hours are considered abandoned.
  • limit and offset: Paginate the returned carts, similar to Medusa's list API routes.

The comments on each property help the AI tool understand what the parameter does.

The middleware validates the query parameters and sets them in the req.validatedQuery property. Learn more in the Request Validation chapter.

Step 2: Create the API Route#

Next, create the API route that retrieves the abandoned carts. Create the file src/api/admin/abandoned-carts/route.ts with the following content:

src/api/admin/abandoned-carts/route.ts
1import {2  AuthenticatedMedusaRequest,3  MedusaResponse,4} from "@medusajs/framework/http"5import {6  ContainerRegistrationKeys,7} from "@medusajs/framework/utils"8import {9  AdminGetAbandonedCartsParamsType,10} from "../../middlewares"11
12type AbandonedCart = {13  id: string14  email: string | null15  currency_code: string16  updated_at: Date17  items: {18    title: string19    quantity: number20    unit_price: number21  }[]22}23
24type AdminAbandonedCartsResponse = {25  carts: AbandonedCart[]26  count: number27  limit: number28  offset: number29}30
31/**32 * List abandoned carts33 *34 * Retrieve carts with an email that customers didn't35 * complete or update within the specified number of hours.36 */37export const GET = async (38  req: AuthenticatedMedusaRequest<39    unknown,40    AdminGetAbandonedCartsParamsType41  >,42  res: MedusaResponse<AdminAbandonedCartsResponse>43) => {44  const query = req.scope.resolve(45    ContainerRegistrationKeys.QUERY46  )47  const { hours, limit, offset } = req.validatedQuery48
49  const abandonedBefore = new Date(50    Date.now() - hours * 60 * 60 * 100051  )52
53  const { data: carts, metadata } = await query.graph({54    entity: "cart",55    fields: [56      "id",57      "email",58      "currency_code",59      "updated_at",60      "items.title",61      "items.quantity",62      "items.unit_price",63    ],64    filters: {65      updated_at: {66        $lt: abandonedBefore,67      },68      email: {69        $ne: null,70      },71      completed_at: null,72    },73    pagination: {74      skip: offset,75      take: limit,76    },77  })78
79  res.json({80    carts: carts as AbandonedCart[],81    count: metadata?.count ?? 0,82    limit,83    offset,84  })85}

Keep the following in mind when you create API routes for the AI tool:

  • Add a comment above the GET function: Medusa uses the comment's first line as the API route's summary and the rest as its description. The AI tool searches these to find the API route that matches your question, so use words you'd use when asking, such as "abandoned carts".
  • Type the request and response: Medusa reads the query parameters from the request's type, such as the second type argument of AuthenticatedMedusaRequest, and the response's shape from MedusaResponse. Without these types, the AI tool doesn't know which parameters the API route accepts.
  • Paginate list responses: Accept limit and offset query parameters, and return count, limit, and offset in the response, like Medusa's list API routes. The AI tool uses them to page through results, and smaller responses are faster to process.
  • Keep the response shallow: Medusa truncates the documented response shape of deeply nested or very large types. Return only the fields the AI tool needs.
Note: Medusa reads the query parameters from the API route's types, not from the middleware. So, you need both the middleware to validate the parameters, and the types in the API route for the AI tool to know about them.

Deploy and Ask Your AI Tool#

Medusa indexes your store's API routes, including custom ones, each time the environment builds. So, the AI tool can only find your new API route after you deploy it.

To deploy the API route, push your changes to the branch of the environment, as explained in the Deployments guide. Once the deployment is live, ask your AI tool a question that the API route answers. For example:

  • "Which carts were abandoned in the last 24 hours in my store?"
  • "List the emails of customers who abandoned their carts in the last 3 days, with their cart items."

The AI tool searches your store's API routes, finds the /admin/abandoned-carts API route, and calls it with the hours query parameter based on your question.

Note: When you update an API route's code, comments, or types, deploy the changes again so the AI tool uses the updated API route.

Troubleshooting#

AI Tool Doesn't Find the Custom API Route#

If the AI tool doesn't find your custom API route, make sure that:

  • The API route's path starts with /admin or /store, and its folder names don't start with an underscore (_).
  • The API route's file exports a GET function.
  • The environment built and deployed after you added the API route. Redeploying an existing deployment reuses its previous build, so Medusa doesn't index the new API route.
  • The comment above the GET function describes the data with the words you use in your question. You can also mention the API route's path in your question, such as "Use the /admin/abandoned-carts API route".

Store API Route Returns an Authentication Error#

The AI tool calls /store API routes as a guest customer. If your /store API route requires a logged-in customer, the AI tool can't call it. Expose the data in an /admin API route instead.

Admin API Route Returns a Permission Error#

The AI tool calls /admin API routes as your admin user. If your API route applies custom permission checks, make sure your admin user is allowed to access it.

Was this guide helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Docs MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break
⇧↵