4. Persisted queries
6m

Overview

In this lesson, we will:

  • Recap MCP and how they become tools
  • Introduce
  • Use a manifest file to create a list of for our

MCP operations: a recap

Let's quickly recap how our queries can be equipped in an AI assistant's arsenal as handy tools.

It all starts with the MCP server, sitting between our APIs and our assistants. Within the MCP server, we can define the various we want to get packaged up as invokable tools. In the process, each of these tools gets a name, a description, and a run-down of any of the inputs it. The MCP server presents these tools to any AI assistants that connect with it!

A diagram showing a GraphQL operation and some questions that give the MCP server information about its purpose and data

This means that an assistant helping out a customer can review its list of tools (the names, descriptions, and required inputs) and use its built-in language processing strength to decide which tool is most appropriate to call at a particular moment.

A diagram showing how the assistant accesses an interface of tools and can invoke them as needed

The assistant makes its selection, tells the server which tool it would like to use, and the MCP server runs the actual logic behind the scenes—in this case, executing the we defined ahead of time.

A diagram showing how the server executes the logic for the invoked tool, sending a query to the GraphQL API

This keeps the amount of detail an assistant needs about our backend system to a minimum. The MCP server houses these , which then get passed to the assistant as its tools.

So, what's the issue?

What we've just recapped is a great way to handle defining ahead of time and exposing them to an assistant as tools. The problem is that most production already have a set of permitted that can be executed against their schema, called persisted queries. And equipping our MCP server with this same list of means duplicating it across different domains—domains that can easily get out of sync and inconsistent.

The Apollo MCP server gives us another option beyond housing all of our permitted within the server itself. When we connect to our account, the server can access the list of permitted we've already defined. No need for manual work keeping our MCP operations in sync with our graph's—with in GraphOS, we can have a single source of truth.

Persisted queries as tools

Check your plan: This course includes features that are only available on the following GraphOS plans: Developer, Standard, or Enterprise.

To see in action, let's return to our 's page in Studio. We can navigate to our 's Persisted Queries by clicking the option from the left-hand menu.

studio.apollographql.com

A screenshot of the persisted queries page in Studio

For our newborn , we should see that we haven't actually defined any yet. Let's click the Create new list button to start defining our first queries.

This triggers a modal where we can provide our list a specific name and description. We'll call ours mcp then hit Create.

studio.apollographql.com

A screenshot of the persisted queries list creation modal

Next, we'll have the option to select a specific that our list should apply to. We've only got one variant of our graph for now, so click the dropdown and select current. Then click Link.

studio.apollographql.com

A screenshot of the persisted queries list creation modal, focused on the variant selection

The next screen provides us with an ID for our . Additionally, we'll see a command we can use to publish a json file of to this list. We've already provided a file to start with in our repository; let's explore that next.

The persisted queries manifest

In the root of your repo, you'll find a file called persisted-query-manifest.json. It contains a JSON object with an array of operations. Each object in this array represents a particular that we're going to include as a . You'll notice that each operation has three properties: a unique id, a name, type and the body of the itself.

One of our persisted queries
{
"format": "apollo-persisted-query-manifest",
"version": 1,
"operations": [
{
"id": "4654ad9c7fed44b407e23605307fec2b6787ba42859ca57c53545a90618c4dfd",
"name": "GetFeaturedListings",
"type": "query",
"body": "query GetFeaturedListings {\n featuredListings {\n id\n description\n costPerNight\n locationType\n numOfBeds\n __typename\n }\n}"
}
// ... other objects
]
}

The manifest of you publish needs to follow a specific format—indicated by the "apollo-persisted-query-manifest" value at the top of the object—and we've generated ours using the @apollo/generate-persisted-query-manifest package as described in the Apollo Client documentation.

Note: The libraries for Web, Kotlin, and iOS each provide a mechanism for generating a manifest file. Check out the official documentation on generation methods for more detail.

Publishing the manifest

We can use another command, rover persisted-queries publish, to publish the in this manifest file to our new list in Studio.

Let's set up that command. In a new terminal window, navigate to the root of your project directory so we can pass a relative path to our JSON file. Here's what the command looks like—be sure to pass in your own unique before running it!

rover persisted-queries publish \
<YOUR GRAPH REF> \
--manifest ./persisted-query-manifest.json

We should see some successful output.

Publishing operations to list mcp for <GRAPH REF> using credentials from the default profile.
Successfully added 3 operations, creating revision 1 of mcp, which contains 3 operations.

Back in Studio, let's refresh the page. We should see three of our successfully registered in our mcp list!

http://studio.apollographql.com

A screenshot of the persisted queries list "mcp" in Studio, with three new operations

Connecting persisted queries to an MCP server

Let's return to our Docker process running the "all-in-one" container. We need to update how we're running the : specifically, we need to tell it to permit as part of its process.

Open up Docker Desktop, and find your running container. Click the three little dots on the right of the container row, then select View files.

A screenshot of the container in Docker Desktop, with the View Files option highlighted from the three dot menu

This takes you to the file structure inside the container. Next, we'll jump into the config file, and right-click on router_config.yaml. Select Edit file.

A screenshot of the configuration file in Docker Desktop, highlighting the Edit file button

Inside, paste the following contents. We want persisted_queries to be at the top root level (the same level as supergraph).

router-config.yaml
supergraph:
listen: 0.0.0.0:4000
introspection: true
include_subgraph_errors:
all: true
# ... other config properties
persisted_queries:
enabled: true

A screenshot of the configuration file in Docker Desktop, highlighting the new contents to enable PQs

With our changes saved, we'll restart the container.

Task!

Now in addition to the messages saying that our API and MCP servers are running, we should see some relevant output about the tools that we registered: the three that we included in our manifest!

Tool GetFeaturedListings loaded with a character count of 482. Estimated tokens: 120
Tool GetUser loaded with a character count of 333. Estimated tokens: 83
Tool GetListing loaded with a character count of 1301. Estimated token: 385

Cool, now let's get Claude all hooked up.

Connecting Claude

Let's return to our Claude application. In the Application tool bar, open up Settings. (On MacOS, this is accessible through the main Claude menu dropdown.)

Selecting Settings from the Claude application menu

Next, we'll select the Developer option from the left-hand menu. In this window we'll see a short overview about Claude's compatibility with servers that use MCP.

To equip Claude with a connection to our MCP server, we'll click the button that says Edit Config.

The Developer tab in Claude's settings, highlighting the Edit Config button

This will open a new file system window, opened to a particular file in the Claude application folder called claude_desktop_config.json. Right-click on this file, and let's open it up in our code editor.

What we'll see is a pretty simple setup to start! If you're running a fresh installation of Claude, you'll probably see an empty mcpServers config:

claude_desktop_config.json
{
"mcpServers": {}
}

Paste in the following configuration:

{
"mcpServers": {
"airlock": {
"command": "npx",
"args": [
"mcp-remote",
"http://127.0.0.1:5000/mcp",
"--transport",
"http-first"
]
}
}
}

Save the file, and return to Claude. We won't see anything right away, so let's restart the application.

Task!

Chatting with Claude

We shouldn't see anything right away in Claude, but if we look closer there's a little button under the chat box where we can see the available tools we've equipped Claude with. We should see all three of our listed here.

A screenshot of Claude showing the three tools

We also recommend turning off the Web search toggle to better isolate how Claude is using our MCP server to answer questions.

Let's ask Claude a basic question about featured listings.

What are the featured listings today?

And as we expect, Claude tries to invoke one of our tools.

A screenshot of Claude asking to invoke a tool

When we approve this, we can confirm that our GetFeaturedListings is working! We can see that a tool has been called, and pretty quickly we'll get that data back. Great, let's try another question: pick one of the featured listings, and ask Claude for some more information about it.

Can you tell me more about the Cozy Yurt in Mraza?

In response, Claude might make two requests: it would be reasonable for our assistant to invoke the GetListing tool for more information about the listing and, upon receiving that data, decide to call GetUserDetails for host information as well. Our tools are working!

We were able to define our in a list directly in Studio. When we pass our credentials to give the MCP server access to this list of , it's able to make tools out of them that assistants can access and call.

Practice

What is the main benefit of using persisted queries as MCP tools in a production graph?

Key takeaways

  • With , we can define a set of we want AI assistants to have access to in the form of tools. This allows us to predefine the operations we permit assistants to run, without worrying about overreach or access to sensitive information.
  • We create a new list of directly in . We can link this list to a particular graph , and then publish a manifest file which contains all the details about each we add.
  • We can publish the using the 's rover persisted-queries publish command.
  • To use , our local needs the persisted_queries property enabled in its router-config.yaml configuration file.

Up next

Let's take things a step further by enabling Claude to introspect our schema, giving it access to all of our 's types and .


Share your questions and comments about this lesson

This course is currently in

beta
. Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.