Transcripted Summary

You open the Inspector. The Tools tab: three tools. The Resources tab: two resources. There's one tab left. Prompts. Still empty. You know what to do.

# What an MCP Prompt Actually Is

We've covered two of the three MCP primitives. Tools are what the model can do. Resources are what the model can read. And prompts are something a little bit different — and the name is kind of misleading. So let's be precise here.

In MCP, a prompt is not a system prompt. It's not the message that you type into a chat window. It's a reusable, parameterized template. It's a named, structured piece of instruction that the host can invoke on demand with arguments substituted in. Think of it as a function that returns a conversation starter shaped exactly for a specific workflow. It's like an AI icebreaker.

Here's the concrete version. You're going to expose a prompt called design_workbook. When a user invokes it from their AI host, they'll pass in a sheet name and a description of the data they want. Your server will then render all of those into a fully formed instruction — something like "generate a workbook named sales with columns for region and revenue, 50 rows of realistic data, include edge cases for nulls and zero values" — without them typing all that out.

That instruction is then handed to the model, which then calls generate_workbook with exactly the right arguments. There's no guessing, there's no ambiguity, no thrashing. That's the value.

Tools give the model capabilities. Resources give it knowledge. Prompts give the model a running start — a prebuilt server-side workflow that a user can invoke with minimal input and still get a predictable, high-quality result.

And there it is. That's the triad. Tools, resources, prompts. Everything an MCP server can expose.

# Three Failure Modes

"I'm tired. Who cares? Why do we need another thing?"

Well, this is where a lot of otherwise well-built servers fall apart in practice.

The first failure mode is wrong tool selection. The model looks at your tool names and descriptions and picks the wrong one, or calls two when one would have been plenty, or skips a tool entirely because the description didn't tell it when to use the thing. The model is making a reasonable inference from the words you gave it. But if those words are vague, then its inference is wrong. And that failure belongs in the description, not the model.

The second failure mode is thrashing. The model calls a tool. The result does not quite answer the question. So it calls another tool, and then another. And then it loops, and you watch your token counter climbing and climbing and climbing, and the context window fills all the way up, and none of them are moving the workflow forward, and you're out of money.

Thrashing almost always traces back to one of two things: a result that did not contain enough information for the model to know what to do next, or a tool description that did not make the expected sequencing clear.

The third failure mode is the model doing manually what your tools were built to do automatically. If you ask it to make a workbook and instead of calling generate_workbook it writes you a Python script, it did not know the tool existed, or it didn't think it applied because the description didn't connect the tool's purpose to the user's intent clearly enough.

All of these are fixable. Most of these fixes are just words.

# Writing the Prompts

So, one more time, let's add a new file. Let's call it WorkbookPrompts.java.

We're going to add a handful of imports. For the MCP server features, we add SyncPromptSpecification just like the others. We're going to use GetPromptResult. We're going to use Prompt, PromptArgument, PromptResult, Role, TextContent, and some Java imports of Map and List.

With these, we can add a prompt. So this SyncPromptSpecification is designWorkbookPrompt. We're going to call it design_workbook. Title: "design workbook." Description: "makes a workbook." No surprises there.

The arguments we'll have are a description and a row count. I want this to guide the prompt to create a workbook of however many rows.

We return a new SyncPromptSpecification with a map of the arguments. We'll turn the value of the description and the row count into a string. Then finally, the string text: "make a [description] workbook with [rowCount] rows. Use generate_workbook."

That's the actual prompt itself. That's what's guiding — being sent over to the LLM to guide its thinking. Whatever kind of workbook you want, however many rows, and telling it, giving it that hint, that nudge, of "use generate_workbook."

This is the kind of hint that wouldn't necessarily be present if you had just queried, "Hey, make me a workbook." The LLM might have access to the generate_workbook tool, but it might not be able to piece those bits together. Things like this make it very obvious.

It ends with the builder — TextContent.builder().text(...) — and sends it along. Pretty straightforward. Just like the others, it has that same kind of builder shape where you're generating the prompt (or the resource, or the tool), the information about it, whatever it actually does, and then returning the message.

Let's make a second prompt too. If the first one's for generating workbooks, let's have the second one be for reviewing them. So review_workbook is going to "review a previously generated workbook's data for quality and consistency with its column specs." It's only going to have one argument: the file name — the name of the generated workbook file.

Similarly, our actual prompt is this line: "review [fileName] and check if the data looks correct." Pretty straightforward. Same thing with the return. It builds the prompt result of whatever the text is.

And that's it. That's all you have to do to generate these prompts. No schema this time. I'm sure there are lots of happy people out there.

# Registering the Prompts

To actually implement these, you'll go inside the WorkbookMcpServer. No new includes this time, no new imports. But this is getting a little unwieldy, so let's tab this out. Within the capabilities builder, I'm going to indent these so they're easier to read. And we're going to add one new one — and I'll even do it live for you: .prompts(...). Probably could have seen that coming.

But this is the full shape of our capabilities. And then to actually register these — you'll never guess — same pattern: server.addPrompt(WorkbookPrompts.designWorkbookPrompt()). There you have it.

Let's do mvn clean compile. Let it go. And voila, build success. Just like that, you have prompts.

# Trying Them Out

So we made them. Let's try them out.

Prompts are a little different. They have to get run through the Claude CLI for now, at least as of time of writing, as opposed to the terminal — that particular terminal doesn't take it very well, but it's a good example to show you how that works.

Inside, I navigate to my code folder, the workbook generator, and I type in claude to open up the Claude CLI. Again, whatever LLM you're using, whatever AI you choose to bring, there's probably some way to set it up that might be different.

I can type slash and I can see workbook-tools:design_workbook. It comes from the MCP server "workbook tools." And this is the design workbook prompt: "makes a workbook." Within this, it shadows in description and rowCount as the two arguments.

For this, I'm going to enter something kind of vague: "make me a comprehensive workbook." And leave it at that. There's a whole second argument that we're going to exclude. Let's just see what happens.

It's going to think — probably a good chance to talk about the specifics of these workbook prompts. We want to give it, as we're demoing this, something vague enough that it gets what's happening, but not so many details that it gets confused.

So it was smart enough to call workbook tools, and generate_workbook. Great — the prompt asked it to do that, and it went and did it. It intuited that a "comprehensive" sample meant a mix of column types, and it made 500 rows. I didn't give it a number, so it just kind of picked one for me. It gives me the shape of what this is going to look like, all the columns it's going to do, all the information.

Let's say yes, call it, and see what happens. It generated 500 rows across 10 columns. Let's take a look. It looks like some kind of customer and region, a product, quantity, unit price. We've got dates, different columns, possibility for it to be empty, all of these kinds of things. I will say 500 samples was larger than I wanted — but shame on me for not specifying it.

Now that that's done, on the flip side, let's review this workbook. It's going to be titled "comprehensive workbook." Let's see what it does.

It's thinking. Okay, so it's identified that list_workbooks is the tool to do this with. It's going to check to see if it's there. Churn, churn, churn. Okay, interesting — it's calling a bash command. "There's no dedicated review workbook MCP tool." Huh. Right?

So the LLM here chose to look for an MCP tool to do the reviewing, as opposed to using the MCP resource that we gave it previously. If we go check out the resources, there's a resource for what the schema looks like. There's a resource to open the workbook file. I think there's even one for the template specification. It should be able to use what it already has to do this — but instead it's choosing to use a Python script.

Okay. So it's taking a bash command and using a Python script. I'm not even sure if I have Python installed, but hey, let's see what it does. It's choosing to inspect the file directly. Oh, you're calling Node now. Do I have Node installed? Well, you can start to see what's happening. We're going to unzip this? What are we doing?

Tokens are counting up. Sure is thinking. Yeah. I hope you can start to see the direction this is taking. The prompt that we wrote was a little vague, and as a result, it's thrashing. This LLM is crashing out at this point. I'm not even sure what it's trying to do. There's the tokens. We're burning them. Look at it go.

I'm going to let this one stop right now. But hey, it did in fact run the prompts. I wonder if we can make them a little bit better.

# Token Efficiency

Every token your server puts into the context window costs something. In a typical back-and-forth conversation with an LLM, that cost is nearly invisible. But in an agentic loop, where the model might call a few tools, read a few resources, and chain a few loops together, it starts to add up pretty fast.

There are two places where the token bloat typically lives in an MCP server.

The first is the tool results. We already looked at that back in part three. The generate_workbook tool returns summary information instead of the whole contents of the file. The model can ask for more if it needs it.

The second place is the tool descriptions. A tool description that's paragraphs long isn't better than one that's two sentences. It's just more tokens the model has to process every single time it considers whether to call the tool. Be specific, not exhaustive. The goal is signal, not volume.

The MCP prompts primitive does a lot of the heavy lifting here too. A good prompt template pre-structures the user's intent before it ever hits the model, which means the model reaches the tool faster, correctly, and with fewer exploratory calls in between. Fewer calls means fewer tool results in the context. That is token savings you get for free just by having a well-designed prompt.

# Improving the Prompts

We've been pretty careful with our tool and resource descriptions, so I'm not too worried about those. But the prompts are pretty vague, and I bet we can improve them.

For the first one, it was just "make a [description] workbook with [rowCount] rows and use generate_workbook." It worked pretty well, to be honest. We don't need a ton of changes, but let's try this out: "design a sample Excel workbook for [description], target row count [rowCount]."

If you remember in the demo, I didn't give it a row count and it just spit out 500 as a guess. Instead, let's have it follow a series of steps to make sure it does these things one at a time and doesn't forget anything:

  1. Confirm that you have both a workbook description and a target row count. If it's not provided, ask the user for it and wait for their answer before continuing.
  2. Read the available resources before choosing the columns.
  3. Choose a file name and a set of columns that reflect the description.
  4. Call generate_workbook.
  5. Call list_workbooks to confirm that the file was made.

So there you have it. That's a much more comprehensive one.

As for the review workbook prompt — this one struggled. It started running Python. Not great. This one's going to need a lot more attention, and that makes sense, because all it said was "review and check if it looks correct." Not particularly helpful.

Instead, let's give it an essay. "Review the generated workbook [fileName] for data quality." Steps:

  1. Confirm the file name above is a real value and ends in the Excel extension. If it isn't, ask the user for a file name and wait for their answer before continuing.
  2. Call list_workbooks to confirm it exists. If it doesn't exist, let the user know.
  3. Read the resource workbook-tools://workbooks/{fileName} to get its contents as a CSV.
  4. Check the data for internal consistency. Make sure there are no issues across it.
  5. Report a verdict of pass or fail.

And: use only the tools and resources provided by this MCP server to perform this review. Don't inspect it with shell commands or the file system. No Python, no Node.

With these two in place, our prompts should be a lot more effective. Not that I'm worried, because we didn't actually change any code — but let's build it just to make sure. Great.

# The Second Run

With these changes, let's try again and see how it does. Go back into Claude. We're going to call design_workbook. Let's do the same thing as last time — "make me a comprehensive workbook" — and still leave the other argument blank.

The very first thing it does is identify that the variable was missing, and it asks me what it should do. Let's give it 100.

So: confirm that you have these; if not, prompt for it. The next thing it should do is double-check the resources. Yep — read resource schema, read resource column types. And it has a workbook for us to look at. Let's proceed. It's probably going to call generate_workbook and then call list_workbooks. Tool use, list_workbooks. So it generated it, now it's calling list. And it's complete.

We have "comprehensive sample," 100 rows, order-style tracking. Let's see how it looks. Yeah, that looks like an Excel sheet. Looks pretty comprehensive to me. 101 rows — so 100 plus a header. It looks like solid data. A good use of integers, decimals, order statuses, order dates, numbers, all these kinds of things.

Now let's get to the good part. This struggled last time. Let's see how review_workbook does this time. And let's give it just "comprehensive sample" — let's not even give it the file extension and see what it does.

So it's looking, and I'll pull this up on the side to follow along. "Confirm the file name is a real file." What's it doing? It calls list_workbooks to see if it's in there. "Call list_workbooks to confirm it exists." Comprehensive sample. Oh, did I have a typo? "Sample Excel generated." Wow, so it spotted that I have a typo in there and that I was missing the file extension. So it said, "which one do you want?"

Excellent. It intuited that I wanted to use the one that was generated earlier. Let's use that. See, little things like that — little ways that you can guide the prompt — can save all those tokens that would've otherwise been wasted if it loaded up the wrong thing or got confused.

And it compared it. Let's see. It checked the resources. Checked the contents of the CSV. Checked for internal consistency. Reported a verdict. Yeah, it used that workbook resource to get the text from it. And it passed. Checked all 100 rows. There are no duplicates in the order. There are consistent names. Different regions are used. Wow, that's really handy. No anomalies found. What a useful tool.

We've gone from thrashing around for a couple of minutes and wasting thousands of tokens to actually getting a valuable, useful result. Again, this is the value of having these kinds of step formats in your prompts and using prompts instead of just asking the LLM directly.

# One Last Look in the Inspector

With our prompts in place, let's see how it looks from the Inspector side. Open up a window, copy over the token, go to the configuration, paste it in, connect it.

We have resources. We have tools. And finally, we have prompts. Inside of it, we see the title and the description. And inside of this, we can enter some default text, call "get prompt," and we can see in this message that it injects "review the generated workbook lorem for data quality."

There you have it. We've got tools, resources, and prompts. That's a hat trick. The server's complete — but doesn't it seem a little lonely?

In the next section, we'll connect it to the Applitools Eyes MCP server and build the full end-to-end workflow we've been working towards since part one. The generator produces the data. The viewer renders it. Eyes confirms it looks right. All orchestrated by an agent talking to two servers through one protocol.

Okay, last section. Let's finish this.



Resources



© 2026 Applitools. All rights reserved. Terms and Conditions Privacy Policy GDPR