March 12, 2026

How to Build AI Agents Using Your GraphQL Schema

Kaitlyn Barnard

Kaitlyn Barnard

AI agents are evolving at a remarkable pace. In just the past year, they’ve gone from experimental demos to production tools capable of planning and executing multi-step workflows. But for most engineering teams, the real question isn’t whether agents are powerful. It’s how to connect them to the applications and data you already have.

If your team already maintains a mature GraphQL API, you’re closer than you think. GraphQL schemas describe your domain in a declarative, structured way, making them a natural fit for AI agents that need context, clear boundaries, and predictable data contracts. The challenge has been bridging the gap between GraphQL’s query language and the natural language reasoning of large language models (LLMs).

Apollo MCP Server solves this by treating your GraphQL schema as a first-class tool in the agent’s environment, without requiring any changes to your existing backend.

In this tutorial, we’ll walk through why GraphQL is uniquely suited for AI agent integration, how the Model Context Protocol (MCP) standardizes agent-to-tool communication, and exactly what it looks like to connect an AI agent to a live GraphQL API using Apollo MCP Server. You’ll leave with a working setup and a clear path to extending it against your own schema.

Why GraphQL and MCP Work Well Together

GraphQL’s strongly-typed schema and built-in introspection give AI agents something most APIs can’t: a complete, machine-readable map of your domain that they can query at runtime. Instead of guessing at endpoint behavior or relying on documentation that may be out of date, agents get deterministic specifications for every operation: what inputs are required, what shape the response takes, and what relationships exist between types.

The Model Context Protocol (MCP) is what makes this accessible to AI applications. It standardizes how agents discover and invoke external tools, so any MCP-compatible AI system can connect to any MCP server without custom integration code. Apollo MCP Server implements this standard for GraphQL, translating your existing operations into callable tools while handling schema validation, protocol translation, and secure execution in between. Your API stays unchanged; agents just get a new way in.

If you want a deeper look at how GraphQL and MCP fit together architecturally, see How to Orchestrate Multiple APIs for Your LLM Agent. For schema design best practices that make your API more effective for AI agents, see How To Make Your Existing GraphQL API AI-Ready With Apollo. This article focuses on the implementation: here’s how to build it.

Tutorial: Connecting an AI Agent to a GraphQL API

To make this concrete, we’ll walk through a full setup using a public GraphQL API that provides information about countries worldwide: https://countries.trevorblades.com/graphql.

We’ll run Apollo MCP Server as a Docker container that exposes GraphQL operations as MCP tools, then connect it to Claude Code (Anthropic’s command-line AI coding tool) so we can query the API using natural language.

Here’s our roadmap:

  1. Introspect the GraphQL schema to understand available data structures
  2. Create a schema file defining the types and queries
  3. Define GraphQL operations that become AI-callable tools
  4. Configure Apollo MCP Server with the endpoint, schema, and operations
  5. Run the MCP server as a Docker container on port 5000
  6. Register the server with Claude Code so it can discover the tools
  7. Test the integration by asking natural language questions that trigger tool calls

Prerequisites: To follow along, you’ll need Docker, the Claude Code CLI, and a local project folder (e.g., mkdir ~/countries-mcp). Alternatively, clone the GitHub repository containing all code used in this walkthrough.

Step 1: Introspect the Schema

GraphQL’s built-in introspection system is what makes it such a natural fit for AI agents. The API tells you exactly what’s possible. Running an introspection query against the endpoint returns a complete representation of the type system:

1query IntrospectionQuery {2  __schema {3    queryType { name }4    mutationType { name }5    types {6      name7      kind8      fields {9        name10        type { name kind }11      }12    }13  }14}

This returns a JSON representation of all types, queries, fields, and relationships in the Countries API, essentially a machine-readable map of your domain that both Apollo MCP Server and your AI agent can reason against.

1{2  "data": {3    "__schema": {4      "queryType": {5        "name": "Query"6      },7      "mutationType": null,8      "types": [9        {10          "name": "Boolean",11          "kind": "SCALAR",12          "fields": null13        },14        {15          "name": "Continent",16          "kind": "OBJECT",17          "fields": [18            {19              "name": "code",20              "type": {21                "name": null,22                "kind": "NON_NULL"23              }24            },25            {26              "name": "countries",27              "type": {28                "name": null,29                "kind": "NON_NULL"30              }31            },32            {33              "name": "name",34              "type": {35                "name": null,36                "kind": "NON_NULL"37              }38            }39          ]40        },41

Step 2: Create the Schema File

Based on the introspection result, create an api.graphql file that defines the schema in GraphQL SDL. Several tools can assist with this: build_client_schema from the graphql-core Python library can construct a schema from an introspection result, or you can use Apollo Studio Sandbox to point at any GraphQL API and copy the schema definition directly.

Save the resulting file as ~/countries-mcp/api.graphql. It will define the core types (Continent, Country, Language, State, Subdivision) along with filter inputs and the root Query type:

1type Continent {2  code: ID!3  name: String!4  countries: [Country!]!5}67type Country {8  awsRegion: String!9  capital: String10  code: ID!11  continent: Continent!12  currencies: [String!]!13  currency: String14  emoji: String!15  emojiU: String!16  languages: [Language!]!17  name: String!18  native: String!19  phone: String!20  phones: [String!]!21  states: [State!]!22  subdivisions: [Subdivision!]!23}2425type Language {26  code: ID!27  countries: [Country!]!28  name: String!29  native: String!30  rtl: Boolean!31}3233type State {34  code: String35  country: Country!36  name: String!37}3839type Subdivision {40  code: String!41  emoji: String42  name: String!43}4445input ContinentFilterInput {46  code: StringQueryOperatorInput47}4849input CountryFilterInput {50  code: StringQueryOperatorInput51  continent: StringQueryOperatorInput52  currency: StringQueryOperatorInput53}5455input LanguageFilterInput {56  code: StringQueryOperatorInput57}5859input StringQueryOperatorInput {60  eq: String61  ne: String62  in: [String]63  nin: [String]64  regex: String65  glob: String66}6768type Query {69  continent(code: ID!): Continent70  continents(filter: ContinentFilterInput): [Continent!]!71  countries(filter: CountryFilterInput): [Country!]!72  country(code: ID!): Country73  language(code: ID!): Language74  languages(filter: LanguageFilterInput): [Language!]!75}

Apollo MCP Server uses this schema to understand the available data structures and how they relate to each other, forming the basis for tool generation.

Step 3: Define GraphQL Operations as MCP Tools

Each GraphQL operation you define becomes a distinct, callable MCP tool that AI agents can discover and invoke. Create an operations subfolder in your project:

1~/countries-mcp$ mkdir operations

For example, to give agents the ability to look up countries by continent, create operations/GetCountriesByContinent.graphql:

1query GetCountriesByContinent($continentCode: String!) {2  countries(filter: { continent: { eq: $continentCode } }) {3    code4    name5    native6    capital7    emoji8    currency9    continent {10      code11      name12    }13    languages {14      code15      name16      native17    }18  }19}

Or a name-based search in operations/SearchCountriesByName.graphql:

1query SearchCountriesByName($searchTerm: String!) {2  countries(filter: { name: { regex: $searchTerm } }) {3    code4    name5    native6    capital7    emoji8    currency9    continent {10      code11      name12    }13    languages {14      code15      name16      native17    }18  }19}

Each operation’s structure (parameters, return fields, and name) becomes part of the tool description that the agent reasons about when deciding which tool to call.

Step 4: Configure Apollo MCP Server

The config.yaml file tells Apollo MCP Server where everything lives and how agents should connect:

1endpoint: https://countries.trevorblades.com/graphql2transport:3  type: streamable_http4  port: 50005operations:6  source: local7  paths:8    - ./operations9schema:10  source: local11  path: ./api.graphql

This configuration connects the dots: the GraphQL endpoint to query, the local schema file for validation, the operations folder defining which tools to expose, and the transport configuration (streamable HTTP on port 5000) for MCP clients to connect over.

At this point, your project structure should look like this:

1~/countries-mcp$ tree2.3├── api.graphql4├── config.yaml5└── operations6    ├── GetAllContinents.graphql7    ├── GetAllCountries.graphql8    ├── GetAllLanguages.graphql9    ├── GetContinentByCode.graphql10    ├── GetCountriesByContinent.graphql11    ├── GetCountriesByCurrency.graphql12    ├── GetCountryByCode.graphql13    ├── GetLanguageByCode.graphql14    └── SearchCountriesByName.graphql

Step 5: Run Apollo MCP Server with Docker

With all files in place, start Apollo MCP Server as a Docker container, following the instructions found here:

1$ docker run   \2    -it \3    --rm \4    --name apollo-mcp-server \5    -p 5000:5000 \6    -v $HOME/countries-mcp/config.yaml:/config.yaml \7    -v $HOME/countries-mcp:/data \8    --pull always \9    ghcr.io/apollographql/apollo-mcp-server:latest \10    /config.yaml

Once running, you’ll see:

1INFO Starting MCP server in Streamable HTTP mode port=5000 address=0.0.0.0

Apollo MCP Server is now live, exposing your GraphQL operations as discoverable MCP tools on port 5000.

Step 6: Register the MCP Server with Claude Code

In a separate terminal, connect Claude Code to the running MCP server:

1~$ claude mcp add --transport http countries http://localhost:5000/mcp23Added HTTP MCP server countries with URL: http://localhost:5000/mcp to local config4File modified: $HOME/.claude.json [project: $HOME/countries-mcp]

This registers the server with Claude Code, making all the country data tools available in your terminal sessions.

Step 7: Query Your GraphQL API with Natural Language

You can now interact with the GraphQL API using natural language. To ensure Claude uses the MCP tools rather than searching the web directly, instruct it not to use its built-in WebSearch tool:

1Claude Code v2.0.12Sonnet 4.5 · Claude Pro34> What MCP servers do you have registered?56● I have the countries MCP server registered. This server provides access to country, continent, and language data through the following functions:78  - GetAllCountries - Get all countries with details9  - GetCountryByCode - Get a specific country by its code10  - SearchCountriesByName - Search for countries by name11  - GetCountriesByContinent - Get countries in a specific continent12  - GetCountriesByCurrency - Get countries using a specific currency13  - GetAllContinents - Get all continents14  - GetContinentByCode - Get a specific continent by code15  - GetAllLanguages - Get all languages16  - GetLanguageByCode - Get a specific language by code1718> For this session, I don't want you to use WebSearch. 1920● Understood. I won't use the WebSearch during this session.

Now ask a question that requires real data:

1> What countries are in North America that start with the letter C? 23● countries - GetCountriesByContinent (MCP)(continentCode: "NA")45╭───────────────────────────────────────────────────────────────────────╮6│ Tool use                                                              │7│                                                                       │8│   countries - GetCountriesByContinent(continentCode: "NA") (MCP)      │9│   The returned value is an array of type `Country`                    │10│   ---                                                                 │11│   type Continent {                                                    │12│     code: ID!                                                         │13│     name: String!                                                     │14│   }                                                                   │15│                                                                       │16│   type Country {                                                      │17│     capital: String                                                   │18│     code: ID!                                                         │19│     continent: Continent!                                             │20│     currency: String                                                  │21│     emoji: String!                                                    │22│     languages: [Language!]!                                           │23│     name: String!                                                     │24│     native: String!                                                   │25│   }                                                                   │26│                                                                       │27│   type Language {                                                     │28│     code: ID!                                                         │29│     name: String!                                                     │30│     native: String!                                                   │31│   }                                                                   │32│                                                                       │33│                                                                       │34│ Do you want to proceed?                                               │35│ > 1. Yes                                                              │36│   2. Yes, and don't ask again for countries - GetCountriesByContinent │37│      commands in /home/alvin/repositories/claude                      │38│   3. No, and tell Claude what to do differently (esc)                 │39│                                                                       │40╰───────────────────────────────────────────────────────────────────────╯4142● countries - GetCountriesByContinent (MCP)(continentCode: "NA")43  ⎿  {                                                              44       "data": {45         "countries": [46     … +832 lines (ctrl+o to expand)4748● The North American countries starting with "C" are:4950  - Canada 🇨🇦51  - Costa Rica 🇨🇷52  - Cuba 🇨🇺53  - Curacao 🇨🇼54  - Cayman Islands 🇰🇾5556> What is the national language of Trinidad and Tabago?5758● countries - SearchCountriesByName (MCP)(searchTerm: "Trinidad and Tobago")5960╭───────────────────────────────────────────────────────────────────────╮61│ Tool use                                                              │62│                                                                       │63│   countries - SearchCountriesByName(searchTerm: "Trinidad and         │64│   Tobago") (MCP)                                                      │65│   The returned value is an array of type `Country`                    │66│   ---                                                                 │67│   type Continent {                                                    │68│     code: ID!                                                         │69│     name: String!                                                     │70│   }                                                                   │71│                                                                       │72│   type Country {                                                      │73│     capital: String                                                   │74│     code: ID!                                                         │75│     continent: Continent!                                             │76│     currency: String                                                  │77│     emoji: String!                                                    │78│     languages: [Language!]!                                           │79│     name: String!                                                     │80│     native: String!                                                   │81│   }                                                                   │82│                                                                       │83│   type Language {                                                     │84│     code: ID!                                                         │85│     name: String!                                                     │86│     native: String!                                                   │87│   }                                                                   │88│                                                                       │89│                                                                       │90│ Do you want to proceed?                                               │91│ > 1. Yes                                                              │92│   2. Yes, and don't ask again for countries - SearchCountriesByName   │93│   commands in /home/alvin/repositories/claude                         │94│   3. No, and tell Claude what to do differently (esc)                 │95│                                                                       │96╰───────────────────────────────────────────────────────────────────────╯9798● countries - SearchCountriesByName (MCP)(searchTerm: "Trinidad and Tobago")99  ⎿  {                                                              100       "data": {101         "countries": [102     … +22 lines (ctrl+o to expand)103104● The national language of Trinidad and Tobago is English.

Success!

What Just Happened

When you asked about Trinidad and Tobago’s national language, Claude Code:

  1. Identified the need for external data and checked available MCP tools
  2. Selected SearchCountriesByName based on its tool description
  3. Invoked the tool with the correct parameter (searchTerm: "Trinidad and Tobago")
  4. Apollo MCP Server validated the query against the schema, executed it against the GraphQL endpoint, and returned structured data via the MCP protocol
  5. Claude synthesized a natural language answer from the JSON response

This architecture keeps concerns cleanly separated. Your GraphQL API remains unchanged. No special agent endpoints, no custom resolvers. The AI agent focuses on reasoning and natural language understanding. Apollo MCP Server sits between them, handling protocol translation, validation, and secure execution.

Production Deployment Patterns

Apollo MCP Server integrates with your existing GraphQL infrastructure, so deployment follows familiar patterns.

Local development with Rover CLI. For rapid iteration, use rover dev –mcp to run Apollo MCP Server alongside your local graph in a single process. This makes it easy to test operations and debug agent behavior during development without additional configuration.

Docker containers. Apollo provides Docker images that work in any containerized environment. Mount your configuration and operations as volumes and the container handles the rest, giving you consistent, reproducible builds across development, staging, and production.

Apollo Runtime Container. For production, the Apollo Runtime Container runs the Apollo Router and MCP Server together. Agent traffic flows through the same routing, caching, and federation infrastructure as your application traffic, ensuring consistent behavior and unified observability.

Serverless environments. Agents running in serverless functions (AWS Lambda, Vercel, Cloudflare Workers) can connect to Apollo MCP Server via its HTTP transport. The stateless MCP protocol makes this straightforward: your function makes HTTP requests to the MCP endpoint just like any other API call.

Observability and Security

When Apollo MCP Server runs within the Apollo Runtime Container alongside Apollo Router, agent queries flow through the same infrastructure as application traffic and surface in Apollo Studio. This gives you visibility into agent query patterns, performance metrics, and error rates through the same monitoring tools you already use, with no separate observability stack required.

Security follows your existing GraphQL model. Apollo MCP Server can be configured to pass authentication tokens and headers to your GraphQL endpoint, ensuring that field-level authorization rules, rate limiting policies, and query complexity analysis apply to agent requests exactly as they do for application traffic. Agents operate as controlled clients governed by your existing security policies.

Performance and Scaling

Agent workloads can differ from typical application patterns. Agents may issue multiple queries in quick succession as they reason through complex tasks. Apollo MCP Server handles this through several mechanisms:

  • Query batching: When agents make multiple independent queries, Apollo MCP Server can batch them into a single request to your GraphQL endpoint, reducing round-trip overhead.
  • Persisted queries: Frequently-used agent patterns can be persisted, reducing network payload and improving cache hit rates. This is especially valuable when multiple agents request similar data across different sessions.
  • Response caching: Your existing Apollo caching configuration applies automatically. If an agent requests data that’s already cached, it gets the cached response, preventing redundant API calls.
  • Horizontal scaling: As agent usage grows, add more Apollo MCP Server instances behind a load balancer. The stateless design makes this straightforward, each instance maintains its own schema cache, and requests can be routed to any available instance.

Moving Forward

AI agents are moving from experimental features to production infrastructure. For teams with existing GraphQL APIs, the path forward doesn’t require rebuilding anything. Your schema already contains the structured knowledge agents need: types, relationships, operations, and constraints.

Apollo MCP Server makes that knowledge accessible through the Model Context Protocol, automatically converting GraphQL operations into tools that any MCP-compatible AI agent can discover and use. Schema introspection is automatic. Query validation is transparent. Observability integrates with your existing monitoring. Security follows your established policies.

The result is an architecture where agents interact with your data in a controlled, observable way while your GraphQL API continues serving applications unchanged. Human users and AI agents operate through the same infrastructure, governed by the same rules.

If you’re already invested in GraphQL, you’re well-positioned to make your API work for the next generation of AI-powered applications.

Written by

Kaitlyn Barnard

Kaitlyn Barnard

Read more by Kaitlyn Barnard