Every route configures itself
A system worth building gets reached more than one way. A person uses it through the web, the mobile app, or the desktop app. A developer reaches the same platform from their own code, an SDK or a bare curl call carrying an API key or a bearer token instead of a login. An AI agent connects through an MCP server and does the work its owner would have done by hand. Each is a real need, and each raises the same questions about every single route in the product. Who is allowed to call this. Is it in the SDK a customer installs. Can a key reach it, or only a signed-in session. Can an agent call it through an MCP server, and if it can, is it safe to let it. A platform is thousands of answers to those questions, one route at a time, and where the answers live decides whether the platform holds together.
The answers usually scatter
In most systems the answers are spread out. The permission check sits in the handler, or in a middleware table read three files away. Whether an endpoint is public was decided in the API gateway. Whether the client library exposes it is a line in a hand-maintained SDK. Whether an agent can reach it is a config file in whatever bolted the agent on last quarter. Each answer is somewhere reasonable. No single place holds all of them for a given route, so no one can look at a route and say with certainty everything that is true about who may call it and how.
None of the scattered answers is a mistake anyone makes on purpose. They are what happens when the answer to who can call this route lives in four places kept by four different habits. The gateway config drifts from the handler check. The hand-written SDK lags the API by a release. The agent scope was set once, under a deadline, and never revisited. Every one was right the day it shipped. The platform's real access rules end up as the intersection of four things nobody reads together.
That intersection is where the incidents live. An endpoint public that should have been internal. A permission enforced in the handler but not in the generated client, so the interface offers a button the API then refuses. An agent handed a tool nobody remembered was destructive.
Who may call it
A check in the handler, or a table three files away
x-permissions, on the route
Reachable by a key
Decided in the API gateway
x-api-key-enabled, on the route
In the customer SDK
A line in a hand-maintained client
x-sdk-enabled, on the route
Callable by an agent
A config file wherever the agent was bolted on
x-tool-enabled and its hints, on the route
The answers live in the schema
Every route in Giant Context is defined by an OpenAPI schema, and the schema is where all of those answers live, next to the route itself, in its x- extensions.
One route's x- configuration
// one route's schema, ordinary fields omitted, these are the x- lines"x-permissions": ["org:settings:write"], // the role required to call it"x-sdk-enabled": true, // present in the customer SDK"x-api-key-enabled": true, // reachable with an API key, not just a session"x-tool-enabled": true, // exposed to an MCP server as a tool"x-tool-read-only-hint": false, // MCP: the tool is not read-only"x-tool-destructive-hint": true, // MCP: it can write, edit, or delete"x-tool-instructions": "...", // MCP: how the tool should be used"x-tool-routes": ["/organizations"], // where it applies, so the in-app chat offers it wellThat block is the whole configuration of the route. Its role requirement, its place in the public SDK, whether an unattended key may call it, whether an agent may call it, and what an agent should know about the risk of calling it are all declared in one place and read straight off the route they govern.
The same block carries more than access. A route exposed to an MCP server brings its own instructions for how the tool is used, and the hints that tell the server whether a call only reads or can change something that cannot be taken back. It carries the routes the tool applies to, so the in-app assistant offers it where it helps and holds it back where it would get in the way, like editing a page from the very dashboard you are editing it in. All of that travels with the route, in the schema, rather than in a separate prompt that drifts from what the route actually does. A route that runs on a clock carries its schedule, and nothing but the scheduler reaches it. A route meant only for development carries a flag that keeps it out of production entirely. A webhook declares the event it answers to. Whatever a route needs the rest of the system to know about it is said here, in the schema, and nowhere else.
Declared once, generated everywhere
Because these are part of the schema, and the schema is what the whole system is generated from, the declarations are not notes. They are the behavior. Generation reads x-permissions and produces the tests that fail the build if the handler does not enforce the role it claims. It reads x-sdk-enabled and emits the SDK method or leaves it out. It reads x-tool-enabled and the hints and registers the route as an MCP tool, passing along whether the call only reads or can change state so the server on the other side knows what it is about to do. The access a route allows, the surface the SDK exposes, and the tool an agent sees are the same fact, read from the same lines.
That single source is what keeps a growing surface honest. The permission a route claims is the permission its generated test asserts, so a handler that checks the wrong role, or forgets to check at all, does not ship. It fails its own test on the branch it was written on. The SDK a customer installs holds exactly the routes marked for it, so the public surface is something the schema states, not something a person remembers to prune. The set of tools an agent can call is the set the schema exposes, never one left reachable by an oversight.
I am not going to walk through how the permission itself is checked, because how a platform enforces its own gate is not something you publish. The gate is declared where anyone can see it is declared, and generated so it cannot quietly disagree with itself. Change one line and the runtime check, the generated test, the SDK, and the agent tool all move on the next build, or fail to compile. There is no second place a permission could drift to, because there is no second place it is written.
Many callers, one set of answers
This is why supporting many callers is not many problems. Every route already answers, in its own schema, who may reach it and through which surface. A person in the browser, a script with a key, and an agent with a granted scope are three callers arriving at the same routes rather than three integrations with three notions of access, and the same x- lines govern all of them. Add a way to sign in and you have added a way to arrive, not a new set of answers to write. The answers were finished when the routes were.
Most platforms treat a new way in as a new thing to secure, so their exposure grows every time their reach does. Here reach and access were never the same concern. Reach is how a caller arrives. Access is what the route already declared. Growing the first does not touch the second.
The simplest credential shows it. A script with an API key reaches exactly the routes that declared themselves reachable that way, gated by exactly the x-permissions a signed-in person meets. Once one caller is governed by the schema, the second and the third are not new engineering but new ways to arrive at routes that already define whether, how, and by whom they can be reached.
The agent is where declaring this early pays for itself. An agent is not a new kind of caller that needs a surface of its own. It is handed the routes that marked themselves callable, as tools, with the read-only and destructive hints the schema already carried. What an agent may reach and what it should fear come from the same lines that governed the route for everyone else, read by one more consumer.
An agent deciding whether to run a tool can read, from the tool itself, whether it only reads or whether it changes something that cannot be taken back. The route describes its own consequences rather than carrying a caution added at the agent layer, and the MCP server calling it is given that description because a machine has no other way to know.
What this is worth commercially
Three consequences follow, and all of them are commercial.
The platform is buildable-on, cheaply and safely. The web, mobile, and desktop apps a customer uses run on the same API their own code can. An API key or a bearer token lets that code reach it, through the SDK, the CLI, or a bare curl call, and an MCP server lets an agent reach it, and every one of them is governed by the routes' own x- lines. I can hand a customer an SDK that contains exactly the routes marked for it, or let an agent connect to exactly the tools marked for it, and neither opens a gap, because neither is a second surface with rules of its own. An integration a customer builds is a customer who is harder to lose, and I can offer it without a standing team keeping two descriptions from drifting apart.
The product also moves faster than its size would suggest. A new capability is a new route with a schema, and the SDK method, the client, the validation, the tests, and the agent tool for it all appear from that one definition. The surface a developer or an agent can use grows by declaration, not by a new integration project each time, and it grows without widening the space where access rules could disagree.
And the platform is ready for where the market is going. The safe way to let an AI act on a business is to let it reach exactly what its owner can reach, knowing exactly which of its actions are destructive, with no path around either limit. A platform that declares reach and risk per route, and generates the agent surface from those declarations, has that by construction. The agent was one more reader of answers the routes already gave rather than a surface I had to go build and secure, and it was cheap and safe for the same reason everything else was. That was decided before an agent was the obvious thing to plan for, and a decision like that only pays off if it was made before it was needed.
Put the answers where the question is
Every route raises the same questions about who may call it and how. Answer them in the one place the route is defined, and generate everything else from the answers. The number of ways to reach the platform can grow as far as the market takes it, because a new caller never needs a new answer. It only needs to read the ones defined in the schema.
Building something like this
I'm Jesse. I build platforms end to end, and I'm open to work. If this is the kind of engineering you need, get in touch.