Skip to main content
Glama

create_fulfillment

Mark items as shipped by creating a fulfillment record for one or more fulfillment orders. Specify per-line item quantities for partial shipments, attach tracking info, and optionally notify the customer with a shipment confirmation email.

Instructions

Mark items as shipped — creates a fulfillment record covering one or more fulfillment orders. For each fulfillment order in the request, you can either fulfill everything still remaining (omit fulfillmentOrderLineItems) or specify per-line {id, quantity} pairs for partial shipments. Optionally attach tracking info (carrier + number; URL is auto-derived for major carriers like USPS/UPS/FedEx/DHL) and set notifyCustomer=true to send the shipment-confirmation email. The fulfillmentOrderLineItem IDs come from list_fulfillment_orders. Side effects: customer-facing email if notifyCustomer is true; webhook fires; remaining quantities decrement.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
lineItemsByFulfillmentOrderYesOne entry per fulfillment order being fulfilled in this shipment.
trackingInfoNoTracking info. Company+number is enough; Shopify auto-derives URL for known carriers.
notifyCustomerNoSend the customer a shipment notification email.

Implementation Reference

  • The handler function for the create_fulfillment tool. Calls the Shopify GraphQL fulfillmentCreate mutation with lineItemsByFulfillmentOrder, trackingInfo, and notifyCustomer. Returns the created fulfillment details.
    async (args) => {
      const fulfillment: Record<string, unknown> = {
        lineItemsByFulfillmentOrder: args.lineItemsByFulfillmentOrder,
      };
      if (args.trackingInfo) fulfillment.trackingInfo = args.trackingInfo;
      if (args.notifyCustomer !== undefined) {
        fulfillment.notifyCustomer = args.notifyCustomer;
      }
    
      const data = await client.graphql<{
        fulfillmentCreate: {
          fulfillment: FulfillmentNode | null;
          userErrors: ShopifyUserError[];
        };
      }>(FULFILLMENT_CREATE_MUTATION, { fulfillment });
      throwIfUserErrors(data.fulfillmentCreate.userErrors, "fulfillmentCreate");
      const f = data.fulfillmentCreate.fulfillment;
      if (!f) {
        return {
          content: [
            { type: "text" as const, text: "fulfillmentCreate returned no fulfillment." },
          ],
        };
      }
      const tracking = f.trackingInfo
        .map((t) => [t.company, t.number, t.url].filter(Boolean).join(" | "))
        .filter(Boolean)
        .join("; ");
      return {
        content: [
          {
            type: "text" as const,
            text: [
              `Created fulfillment ${f.name} [${f.status}] — ${f.id}`,
              f.order ? `  Order: ${f.order.name} (${f.order.id})` : "",
              tracking ? `  Tracking: ${tracking}` : "",
            ]
              .filter(Boolean)
              .join("\n"),
          },
        ],
      };
    },
  • Zod schema defining the input parameters for create_fulfillment: lineItemsByFulfillmentOrder (array of fulfillment order line items with optional per-item quantities), trackingInfo (optional carrier details), and notifyCustomer (optional boolean).
    const createFulfillmentSchema = {
      lineItemsByFulfillmentOrder: z
        .array(lineItemByFulfillmentOrderSchema)
        .min(1)
        .describe("One entry per fulfillment order being fulfilled in this shipment."),
      trackingInfo: trackingInfoSchema.optional(),
      notifyCustomer: z
        .boolean()
        .optional()
        .describe("Send the customer a shipment notification email."),
    };
  • Registration of the create_fulfillment tool on the MCP server via server.tool(), with name, description, schema, and handler.
      server.tool(
      "create_fulfillment",
      "Mark items as shipped — creates a fulfillment record covering one or more fulfillment orders. For each fulfillment order in the request, you can either fulfill everything still remaining (omit `fulfillmentOrderLineItems`) or specify per-line {id, quantity} pairs for partial shipments. Optionally attach tracking info (carrier + number; URL is auto-derived for major carriers like USPS/UPS/FedEx/DHL) and set notifyCustomer=true to send the shipment-confirmation email. The fulfillmentOrderLineItem IDs come from list_fulfillment_orders. Side effects: customer-facing email if notifyCustomer is true; webhook fires; remaining quantities decrement.",
      createFulfillmentSchema,
      async (args) => {
        const fulfillment: Record<string, unknown> = {
          lineItemsByFulfillmentOrder: args.lineItemsByFulfillmentOrder,
        };
        if (args.trackingInfo) fulfillment.trackingInfo = args.trackingInfo;
        if (args.notifyCustomer !== undefined) {
          fulfillment.notifyCustomer = args.notifyCustomer;
        }
    
        const data = await client.graphql<{
          fulfillmentCreate: {
            fulfillment: FulfillmentNode | null;
            userErrors: ShopifyUserError[];
          };
        }>(FULFILLMENT_CREATE_MUTATION, { fulfillment });
        throwIfUserErrors(data.fulfillmentCreate.userErrors, "fulfillmentCreate");
        const f = data.fulfillmentCreate.fulfillment;
        if (!f) {
          return {
            content: [
              { type: "text" as const, text: "fulfillmentCreate returned no fulfillment." },
            ],
          };
        }
        const tracking = f.trackingInfo
          .map((t) => [t.company, t.number, t.url].filter(Boolean).join(" | "))
          .filter(Boolean)
          .join("; ");
        return {
          content: [
            {
              type: "text" as const,
              text: [
                `Created fulfillment ${f.name} [${f.status}] — ${f.id}`,
                f.order ? `  Order: ${f.order.name} (${f.order.id})` : "",
                tracking ? `  Tracking: ${tracking}` : "",
              ]
                .filter(Boolean)
                .join("\n"),
            },
          ],
        };
      },
    );
  • The ShopifyClient.graphql() method used by the handler to execute the GraphQL fulfillmentCreate mutation against the Shopify Admin API.
    async graphql<T>(
      query: string,
      variables?: Record<string, unknown>,
    ): Promise<T> {
      const res = await fetch(this.endpoint, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Shopify-Access-Token": this.accessToken,
        },
        body: JSON.stringify({ query, variables }),
      });
    
      if (!res.ok) {
        throw new Error(
          `Shopify API ${res.status}: ${await res.text()}`,
        );
      }
    
      const body = (await res.json()) as GraphQLResponse<T>;
      if (body.errors?.length) {
        throw new Error(
          `Shopify GraphQL errors: ${body.errors.map((e) => e.message).join("; ")}`,
        );
      }
      if (!body.data) {
        throw new Error("Shopify returned no data");
      }
      return body.data;
    }
  • The GraphQL mutation string used by the create_fulfillment handler.
    const FULFILLMENT_CREATE_MUTATION = /* GraphQL */ `
      mutation FulfillmentCreate($fulfillment: FulfillmentInput!) {
        fulfillmentCreate(fulfillment: $fulfillment) {
          fulfillment {
            id
            status
            name
            createdAt
            trackingInfo { company number url }
            order { id name }
          }
          userErrors { field message }
        }
      }
    `;
Behavior5/5

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

With no annotations provided, the description fully carries the burden. It explicitly discloses behavioral traits: customer-facing email if notifyCustomer is true, webhook firing, and remaining quantities decrementing. These side effects are critical for an agent to understand and are clearly stated.

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

Conciseness4/5

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

The description is a single dense paragraph that front-loads the primary action. It is concise with no wasted words, but could be slightly improved with bullet points for readability. Overall, it efficiently conveys all necessary information.

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

Completeness4/5

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

Given the absence of annotations and output schema, the description covers purpose, usage, parameters, and side effects adequately. It does not describe return values, but with no output schema expected, this is acceptable. It provides sufficient context for an agent to use the tool correctly.

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?

Although schema coverage is 100%, the description adds significant value beyond the schema. It explains that omitting fulfillmentOrderLineItems fulfills everything remaining, that tracking URL is auto-derived for major carriers, and that notifyCustomer triggers an email. This enhances understanding beyond the raw schema definitions.

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?

Description clearly states 'Mark items as shipped — creates a fulfillment record' with a specific verb and resource. It further specifies scope ('covering one or more fulfillment orders') and distinguishes from siblings like cancel_fulfillment or update_fulfillment_tracking by clearly indicating creation of a fulfillment record.

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?

Description provides guidance on when to use partial vs full fulfillment ('omit fulfillmentOrderLineItems for full') and references sibling tool list_fulfillment_orders for obtaining line item IDs. However, it does not explicitly state when to use this tool over alternatives like update_fulfillment_tracking or cancel_fulfillment.

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/miller-joe/shopify-mcp'

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