# Looking Through Claude's Context Window

You've had a Claude Code session get noticeably worse the longer it ran. Slower, vaguer, forgetting a constraint you set three messages ago and confidently doing the opposite. That's not you imagining it. It's the context window, the working memory the entire conversation lives inside, quietly filling up and changing how well the model can actually use what's in there. If you haven't read [What Is a Token?](https://downtherabithole.dev/what-is-a-token) yet, worth doing first, tokens are the unit everything below gets measured in.

So let's open it up and look at what's actually in there.

## The window grows on every single turn

![](https://cdn.hashnode.com/uploads/covers/698ce732e249cba68b3bbdb5/6cbe3446-e908-46c8-a06d-313261b2b054.png align="center")

Turn one of a new session starts blank. You type a message, Claude answers, and that exchange, your input plus its output, becomes the first sliver of context. Turn two adds a new message on top of everything from turn one and sends the lot back to the model. Turn three stacks on top of that. Nothing gets removed along the way. The window just grows, one full turn at a time, dragging the entire conversation along with it on every exchange.

> It's not unlike a conversation with a friend. You both build up shared context turn by turn, and you can reference something they said three exchanges ago because it's still sitting in your working memory. Claude's context window works the same way, minus the part where your friend stops listening halfway through.

Which is exactly why it can't be unlimited. Just like a real conversation, there's only so much that can be held onto, and the more that's crammed in there, the harder it gets to pick out what actually matters. That's the seed of a bigger problem this series will come back to, context rot: more context doesn't mean better recall. Past a point it means noticeably worse.

## Your session was never empty

Here's the bit that catches people out. Run `/context` in a brand new session, before typing a single word, and it's already got plenty in it:

![](https://cdn.hashnode.com/uploads/covers/698ce732e249cba68b3bbdb5/c32075d1-5cc8-4cce-8757-de5fb589de67.png align="center")

The [**system prompt**](https://platform.claude.com/docs/en/release-notes/system-prompts) and **system tools** load first. These are Anthropic's, not yours, and they're not negotiable: a fixed set of behavioural rules the model won't deviate from no matter how you phrase things. Anthropic publishes them, incidentally, worth a read if you've never seen what's actually sitting at the top of every session you run. They also take precedence over anything you write, and it's worth being precise about why, because the intuitive explanation is wrong. It's not that they sit at the top of the context and get read first. It's that the model was trained to weight instructions coming from that layer above anything arriving in a user turn. Position has nothing to do with it.

Then **CLAUDE.md**, if your project has one. This is the one piece of pre-loaded context that's genuinely yours: the standards, tools, and conventions Claude has no way of inferring on its own. It sits at the project root and gets read fresh every session, which is the entire point. Write it once instead of re-explaining your project every time you open a new conversation.

Sitting next to it is something similar but written by the other party: a **memory file**, authored by Claude rather than you, collecting build commands, debugging insights, and preferences it's picked up while working in that repository. Also loaded automatically. Think of it as Claude keeping its own notebook on your project while you're not looking.

## Tools, skills, and MCP walk into a context window

These three get conflated constantly, and it's worth pulling them apart properly, because they behave completely differently once they're in there.

**Tools** are the simplest: functions Claude already knows how to call. Some run locally on your machine (client tools, reading a file), some run on Anthropic's infrastructure (server tools, fetching a web page). The important part is that their definitions load fresh on every single invocation, whether you touch them that turn or not. Tools are always in the room.

**Skills** are a different animal. A skill is a markdown file following the open [agent skill standard](https://agentskills.io/home), packaging up a repeatable procedure: how your team writes documentation, what a PR review has to check, the company-specific detail Claude could never guess. They can call tools to do the actual work, or not. What makes them interesting here is how they load. Only a skill's name and one-line description sit in context at session start, a rounding error in token terms, just enough for Claude to know the option exists. The full instructions only get pulled in if it decides the skill is actually relevant. And they don't necessarily stay: invoke several across a long session, hit a compaction, and the oldest one gets dropped first to make room. If you'd rather a skill never got picked up on Claude's own judgement, `disable-model-invocation: true` removes it from that list entirely, invisible until you call it by name.

> Roughly: a tool is a knife already on the counter, a skill is the recipe card you only pull out of the drawer when you're actually cooking that dish, and MCP is the phone number for the restaurant down the road that'll do the whole thing for you.

Which brings us to **MCP**, the one where popular advice is most likely to be out of date. [Model Context Protocol](https://www.anthropic.com/news/model-context-protocol), created by Anthropic, standardises how an agent discovers and calls tools that live on somebody else's server entirely. [GitHub's MCP server](https://github.com/github/github-mcp-server) is the obvious example: ask for a PR, and instead of your agent picking its way through GitHub's REST API endpoint by endpoint, it calls a `create_pull_request` tool that it already knows exists, and the server handles the messy part. The received wisdom used to be that connecting a handful of MCP servers meant permanently loading every tool's full parameter schema into your context, used or not. That was genuinely true. It isn't anymore. MCP now behaves much closer to skills: tool names load upfront so Claude knows what's on offer, full schemas stay deferred until something actually needs them. If you read a guide six months ago telling you MCP servers are context hogs, that guide is describing a version of the product that no longer exists.

![](https://cdn.hashnode.com/uploads/covers/698ce732e249cba68b3bbdb5/74dff59c-3a26-4b3b-9069-92f41aee74ff.png align="center")

## Sub-agents get their own window entirely

Now the useful part. The thread you're talking to can spin off a child agent to go and handle something on its own: read through a huge file, chase down some research, whatever. That child works in a completely separate context window. Skills and MCP tools get loaded into its context fresh, same as yours. When it finishes, it hands back a summary rather than the raw work, which means every token it burned along the way never lands in your conversation at all.

That's not a minor detail, it's probably the single most effective lever in here. Reading a large file into your main thread costs you that file, permanently, for the rest of the session. Sending an agent to read it and report back costs you a paragraph.

## What happens when it fills up anyway

Manage all of the above perfectly and context still fills up eventually, so Claude Code compacts before it hits the model's ceiling. The exact trigger isn't one flat number, it moves with the model and its configured window. On a full 1M-token window, compaction tends to kick in around 967K. On a 200K configuration it fires much earlier, roughly 83% of the limit. What holds in both cases is the shape: there's always a reserved buffer, and compaction never waits for the last token.

When it fires, older parts of the conversation get summarised down to reclaim space. `/compact` triggers that yourself, and `/compact <message>` lets you steer what the summary bothers to keep. `/clear` is the blunt instrument, wiping working context rather than compressing it. Worth knowing that `/clear` isn't quite as final as it sounds: memory files live outside the context window, so anything Claude already committed to memory survives even when the conversation doesn't.

## So what's actually in the context window?

Strip out the mechanics and it's this: everything the model can currently see and reason from, in one place. Not its training, that was baked in long before your session started and stays a black box regardless. Just this conversation. The system layer that's always present, whatever you put in CLAUDE.md, whatever Claude wrote to memory, every message either of you has sent, and a live, on-demand slice of the tools, skills, and MCP servers you've actually reached for.

Every one of those pieces costs tokens the moment it loads, which is where the [previous post](https://downtherabithole.dev/what-is-a-token) comes back in. But cost is only half of it. The reason to care about a tidy context window isn't just a smaller bill, it's that a model given exactly what it needs answers better than one handed everything you've accumulated over two hours and asked to work out which parts mattered. That's where this series goes next.

* * *

*One number worth holding loosely: the exact point auto-compaction fires varies by model and configuration, and Anthropic has changed it before without much announcement. The durable fact is the shape of it, there's always a reserved buffer and compaction never waits for the literal limit, not any specific percentage or token count quoted above.*
