Skip to content

docs: add schema search RFC - #187

Open
DaleSeo wants to merge 4 commits into
graphql:mainfrom
DaleSeo:schema-search-rfc
Open

DaleSeo wants to merge 4 commits into
graphql:mainfrom
DaleSeo:schema-search-rfc

Conversation

@DaleSeo

@DaleSeo DaleSeo commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Adds an RFC for Schema Search, an HTTP endpoint (GET /graphql/schema?q=…) that returns a valid subset of a GraphQL schema as SDL for a natural language query.

@DaleSeo
DaleSeo marked this pull request as ready for review October 21, 2026 14:18
@DaleSeo

DaleSeo commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

Hey @michaelstaib, I put together a draft proposal based on our discussions since the last meeting. I wanted to list you as a champion too, since so much of the proposal was inspired by you taking the time to walk me through it at Summit. 🙏 The patch-schema idea was basically yours, and the evidence section is based on @PascalSenn's experiments. I also included @martinbonnin's QUERY PR.

Comment thread rfcs/schema-search.md Outdated
| Parameter | Required | Meaning |
| --------- | -------- | -------------------------------------------------------------------------------------------------------- |
| `q` | No | A natural language description of the capability the client needs. MAY be repeated; see below. |
| `v` | No | The GraphQL specification edition whose SDL syntax the client understands, for example `september-2025`. |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would advise that this 1. uses the explicit tag name of the spec (September2025 rather than september-2025), and 2. allows additions for experimental features, e.g.

"v": "September2025+directivesOnDirectives"

Spec revisions can then be thought of as "presets", e.g. September2026 = September2025 + directivesOnDirectives

If the client is newer than the server, the server can use the newest spec version it supports (knowing spec versions will always be MonthYYYY); alas this means the experimental features it supports would be excluded by default (because there's no guarantee those features will have made it into the next version of the spec).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benjie Do you see a use for v? like, especially with agents, what's the problem when the SDL would be "newer"... if we introduce structs, the agent would probably figure this out on their own. I think v would also be very tricky to implement for server maintianers... as you ususally ahve only one way to print the schema.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, @benjie!

On 1, I was following Hot Chocolate's current format, but we can to switch it to MonthYYYY.

On 2, I like the +feature form. Who would define the feature names, though? Are they already documented somewhere?

Comment thread rfcs/schema-search.md
Comment on lines +298 to +302
it. The service reads the repeated `q` parameter with `getAll`. As in GraphQL
over HTTP, an empty parameter is the same as an absent parameter. So `?q=` is a
request without `q`. In a `QUERY` or `POST` request, the body carries the same
parameters as JSON members ([section 1](#1-endpoint)). An empty array
(`"q": []`) is the same as an absent `q`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is repeated parameter support like this universal? I know some systems do things like ?q[]=foo&q[]=bar&.... I feel like PHP and Ruby on Rails at least would struggle with the ?q= syntax for repetition?

I'd advise finding a more universal form of repetition.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think supporting q[] as the parameter name when repetition is used would actually be fairly universal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, @benjie. I checked how common parsers handle q=a&q=b. URLSearchParams.getAll, Python's parse_qs, Node's querystring, Go's url.Values, and Java servlets all return both values. OpenAPI also uses this format by default for array parameters. Rails and PHP, on the other hand, keep only the last value by default and expect q[].

So I don't think there's one universal format. Rails produces q[]=a&q[]=b, while PHP's http_build_query produces q[0]=a&q[1]=b. That means q[] works for Rails but not PHP, and the WHATWG format is the only one backed by a standard. A Rails or PHP service can still read the raw query string, for example, with Rack::Utils.parse_query.

How about we keep repeated q as the standard form that clients send and add that a service SHOULD also accept q[] as the same parameter?

@DaleSeo

DaleSeo commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks to everyone for the feedback at the October meeting. 🙏 Below is my summary. Please let me know if I got anything wrong.

Agreed

  • @magicmark and @zkozina were fine with the name q.
  • The guarantee is a valid slice that helps an agent (or even a human) build a valid operation without guessing. But the spec can’t guarantee that the operation the agent builds will answer the question.
  • The reasoning stays on the client. The server returns a slice, not a finished operation.
  • A request without q returns the full schema. @PascalSenn and @magicmark asked to make this a requirement instead of a MAY. Then the endpoint is also a standard way to get the SDL without introspection.
  • The service accepts a body method (QUERY or POST) from the start, not only for incremental expansion. A body avoids "URI too long" errors, and it keeps prompts out of URLs and logs.
  • @DaleSeo was concerned about caching. @PascalSenn said that the full schema is easy to cache. But the same natural language search seldom comes twice, so an edge cache gives little benefit for searches.
  • The service returns 204 No Content when nothing matches. A 404 tells the client that the endpoint does not exist. A clear "nothing matches" response stops the agent from retrying a question that the schema cannot answer.
  • The search algorithm stays implementation-defined. @PascalSenn found that the ranking algorithm has more effect than the embedding model. Search also works well without an LLM.
  • The response uses text/plain for now. The response is plain SDL, not a GraphQL response, so application/graphql-response+json does not apply. We can define a dedicated media type later, and this work does not have to wait for it. The same applies to patches.
  • Discovery belongs in the capabilities API, which is still an RFC. By default, the search URL is /graphql/schema. A service can use a capability to point to a different URL, even on another host. Without a skill or built-in support in the agent harness, a client can’t find the endpoint.

Open

  • v: Pascal asked if we need it at all. LLMs can usually read newer SDL. Most servers also have only one schema printer, so they cannot easily print new syntax, such as structs, for an older edition. Pascal thinks that a version marker in the SDL itself belongs in the main spec.
  • Incremental expansion: @PascalSenn prefers to focus on one request and isn’t sure a second request would help. Patches work well when the agent harness supports them natively, for example, by providing a dedicated place for the schema in the context. Without that support, an agent can waste output tokens writing the known schema into a JSON body.

Action Items

  • Make the full-schema response a requirement.
  • Require support for a body method from the start, and explain the trade-off for caching.
  • Use text/plain as the response media type for now.
  • Add a complete example of an HTTP request and response, as @magicmark asked.
  • Note the spec proposals that allow types without fields, which @PascalSenn raised. The released spec still requires at least one field. Object types (Allow empty object types graphql-spec#1228) and interfaces/unions (Allow interfaces with no fields, unions with no members graphql-spec#1229) are proposals in progress, and input objects have no PR yet. So the RFC mentions them as proposals, not as current rules.
  • State the goal more strongly. The first response must contain enough to answer the question. Sessions with many turns filled the context, and cheaper models lost track of the task.
  • State in the RFC that discovery is out of scope. We can solve it later with the capabilities API or native support in agent harnesses.

Next steps

@PascalSenn suggested adding this RFC to the next agenda and inviting @benjie. Then we can decide whether the spec belongs in this working group or in GraphQL over HTTP. If we go with GraphQL over HTTP, we’ll also need @martinbonnin. The November 26 and December 24 meetings are cancelled, so the next regular meeting is January 28, 2027. @PascalSenn, is there a chance to discuss this earlier?

Separately, @PascalSenn offered to propose an @example directive that adds example values and example operations to the schema. @magicmark supports the idea.

Comment thread rfcs/schema-search.md Resolved
Comment thread rfcs/schema-search.md Resolved
Comment thread rfcs/schema-search.md

- A service can set its search policy independently of its introspection policy.
- A harness can construct a URL without a GraphQL client.
- The response is SDL text, not SDL inside a JSON string.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main advantage to me. No need to wrap/unwrap JSON.

Is that sufficient to add another endpoint?

Another thing to consider is telemetry. Query.__schemaSlice would get logged into insights automagically. Using a separate endpoint means monitoring somewhere else. Maybe it's desired, maybe not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, that's a fair question. TBH, I don't think I gave enough thought to why this has to be an endpoint. I'm not attached to the endpoint if there's a better way to avoid relying on introspection, since it's recommended to be turned off in production.

BTW, Query.__schemaSlice might be forbidden by the spec because it starts with __? 🙄

@michaelstaib Is there a stronger reason to add an endpoint that I might have missed in our discussions?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Query.__schemaSlice might be forbidden by the spec because it starts with __? 🙄

The whole reason we reserve __ is for the spec to be able to add new types/fields. It's forbidden for a end-user "source schema" to define those but as spec authors, we could decide to do it.

Note that this currently will show errors in graphiql and other tools because there is no way to validate a full schema but this is something that needs to be adressed separately IMO.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that clears it up! I understand now that, technically, the spec doesn't block a __schemaSlice field. The only open question is which shape we prefer. I'm still leaning toward an endpoint because it works today without waiting for the tooling to catch up, and the SDL comes back as plain text instead of a JSON string.

@martinbonnin martinbonnin Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only open question is which shape we prefer.

Yup.

I'm still leaning toward an endpoint because it works today without waiting for the tooling to catch up

Fixing graphql/graphiql#3968 is required if we want to evolve GraphQL.

Separate endpoint adds new tooling requirement (telemetry, IDE/GrpahiQL, discovery) instead of fixing the existing one.

I'd say this is a very good opportunity to actually improve the existing tooling.

The SDL comes back as plain text instead of a JSON string.

This is a big plus.

Another aquestion: do we want this to be served by the runtime vs control plane? Maybe introspection being part of the runtime itself was an error in GraphQL?
But now that it's there, I think I'd favor __schemaSlice for consistency (also simplicity).

More philosophically, and sorry in advance for the Yak shaving, but I think GraphQL lacks a "control plane" for stuff like registering persisted queries and field usage monitoring. I would love to see a graphql-control-plane.js. Maybe this one could expose the SDL endpoint...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a clear preference, @martinbonnin. The tooling point is fair. I also agree with your telemetry concern.

I hadn't thought about this in terms of the runtime and control plane, so thanks for the insight. 💡 but you know what? It actually makes me hesitant to make this a field. 😅 The data behind search, like embeddings and usage weights, is built when the schema is published and isn't tied to executing operations, right? A gateway or schema registry could serve /graphql/schema without executing GraphQL, but Query.__schemaSlice would need to be part of the executable schema.

I'd rather not settle this between the two of us, though. @michaelstaib is a co-champion, so I'd like to hear his view too.

@martinbonnin martinbonnin Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data behind search, like embeddings and usage weights, is built when the schema is published and isn't tied to executing operations, right?

Agreed. This all points to a separate endpoint. For the record, I'm not opposed to this, just brainstorming. /graphql/schema also has the advantage (and drawback) that it can be adopted without changing the main GraphQL spec. Just make it a GAP and then servers/registries could start implementing it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants