Skip to content

feat(router): expose prompt-to-query via MCP - #3158

Open
fiam wants to merge 14 commits into
wilson/cosmo-335-cosmo-cloud-prompt-to-query-via-mcp-productfrom
alberto/router-575-router-mcp-query-generation-support
Open

feat(router): expose prompt-to-query via MCP#3158
fiam wants to merge 14 commits into
wilson/cosmo-335-cosmo-cloud-prompt-to-query-via-mcp-productfrom
alberto/router-575-router-mcp-query-generation-support

Conversation

@fiam

@fiam fiam commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • expose a generate_query tool through the router MCP server
  • send the active RouterConfig.version to the control-plane GenerateQuery RPC
  • resolve that version to the stored SDL in the control plane, call Yoko EnsureIndex on demand, and use its returned opaque index ID
  • add OAuth scope support and MCP documentation for the tool

Stack

Verification

  • CGO_ENABLED=0 go test ./... in router
  • control-plane TypeScript build

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bbbe0658-a9f1-4bc2-a882-387cca981824

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

❌ Internal Query Planner CI checks failed

The Internal Query Planner CI checks failed in the celestial repository, and this is going to stop the merge of this PR.
If you are part of the WunderGraph organization, you can see the PR with more details.

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 36.57143% with 111 lines in your changes missing coverage. Please review.
✅ Project coverage is 43.96%. Comparing base (c9d6d43) to head (064d7d2).

Files with missing lines Patch % Lines
router/pkg/controlplane/prompttoquery/client.go 0.00% 44 Missing ⚠️
...rolplane/src/core/services/PromptToQueryService.ts 10.00% 27 Missing ⚠️
router/pkg/mcpserver/server.go 71.08% 16 Missing and 8 partials ⚠️
router/core/supervisor_instance.go 0.00% 7 Missing ⚠️
router/core/router.go 0.00% 4 Missing and 1 partial ⚠️
router/gen/proto/wg/cosmo/node/v1/node.pb.go 0.00% 2 Missing ⚠️
controlplane/src/core/bufservices/NodeService.ts 0.00% 1 Missing ⚠️
router/core/graph_server.go 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@                                       Coverage Diff                                        @@
##           wilson/cosmo-335-cosmo-cloud-prompt-to-query-via-mcp-product    #3158      +/-   ##
================================================================================================
+ Coverage                                                         43.85%   43.96%   +0.10%     
================================================================================================
  Files                                                              1072     1078       +6     
  Lines                                                            140577   141078     +501     
  Branches                                                           7361     7446      +85     
================================================================================================
+ Hits                                                              61655    62020     +365     
- Misses                                                            77071    77190     +119     
- Partials                                                           1851     1868      +17     
Files with missing lines Coverage Δ
router/core/router_config.go 93.97% <ø> (ø)
router/pkg/config/config.go 83.00% <ø> (ø)
router/pkg/mcpserver/auth_middleware.go 90.69% <100.00%> (+0.10%) ⬆️
controlplane/src/core/bufservices/NodeService.ts 19.17% <0.00%> (ø)
router/core/graph_server.go 85.76% <0.00%> (ø)
router/gen/proto/wg/cosmo/node/v1/node.pb.go 28.69% <0.00%> (+1.13%) ⬆️
router/core/router.go 70.76% <0.00%> (-0.23%) ⬇️
router/core/supervisor_instance.go 0.00% <0.00%> (ø)
router/pkg/mcpserver/server.go 74.54% <71.08%> (+3.98%) ⬆️
...rolplane/src/core/services/PromptToQueryService.ts 27.86% <10.00%> (-4.41%) ⬇️
... and 1 more

... and 23 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread controlplane/src/core/services/PromptToQueryService.ts Outdated

// Invoke the `prompt to query` service
try {
const indexId = await this.ensureIndex(schemaVersion.sdl);

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 method is not intended to be called by the query generator. The reason for this is that the service could take a bit to parse and index the schema, use the crypto to generate the SHA256 for the schema and use the value directly.

If the index doesn't exist, let it fail

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.

Makes sense! PTAL and let me know if this is what you had in mind. Thanks!

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.

ensureIndex is implemented via a task queue so it should actually respond instantly with most fields, but importantly query generation will error if the index is not completed so you may want to poll here or fail fast.

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.

In the context of Yoko that is correct, however, this is just a helper for ControlPlane to send the schema for indexation. If we want to check whether the index is already there, we need to make a new helper

@fiam
fiam marked this pull request as ready for review August 13, 2026 08:56
@fiam
fiam requested review from a team as code owners August 13, 2026 08:56
Comment on lines +163 to +171
if cfg.MCP.Enabled && cfg.Graph.Token != "" {
promptToQueryClient, err := prompttoquery.New(cfg.ControlplaneURL, cfg.Graph.Token,
prompttoquery.WithLogger(logger),
)
if err != nil {
return nil, fmt.Errorf("could not create prompt-to-query client: %w", err)
}
options = append(options, WithPromptToQueryClient(promptToQueryClient))
}

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.

Unsure about this, see my PM


const defaultTimeout = 15 * time.Second

type Option func(*Client)

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 this worth it for just a logger and timeout vs named parameters to New(...)? This is internal-only, the default is either used or it isn't

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.

I agree, probably a bit overkill. Hardcoded the timeout and made the logger a required argument. I've also moved the package to be internal so we don't have to care about a stable interface b7f1b0f - PTAL

Comment thread router/internal/prompttoquery/client.go Outdated
Comment on lines +53 to +62
retryClient := retryablehttp.NewClient()
retryClient.RetryWaitMax = 15 * time.Second
retryClient.RetryMax = 3
retryClient.Backoff = retryablehttp.DefaultBackoff
retryClient.Logger = nil
retryClient.RequestLogHook = func(_ retryablehttp.Logger, _ *http.Request, retry int) {
if retry > 0 {
c.logger.Info("Generate query through controlplane", zap.Int("retry", retry))
}
}

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.

Need to ensure that this does not retry on errors that should not be retryable

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.

The default policy retries on 429 and >= 500 (except 501). Looking at the CP side, it seems we only return 5xx for unhandled errors, all application errors return 200.

So the only thing that could generate a retry is either an intermediate proxy/infra or either an unhandled exception (which might or might not be worth retrying). However, some errors in k8s ingress or some service meshes also end up generating a 500 when the service is unavailable (e.g. some rollout strategy not properly configured).

Since prompt-to-query is supposed to be idempotent I think it might be safer to over-retry than under-retry (if that makes sense). WDYT?

Comment thread router/internal/prompttoquery/client.go Outdated
Version: schemaVersionID,
Prompt: prompt,
})
req.Header().Set("Authorization", "Bearer "+c.graphAPIToken)

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 believe this is better done as part of a roundtripper on the client itself

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.

I think that's a cool idea, but as an internal package, so eventually all communication to the control plane could use the same transport. PTAL 561f7e2

"prompt": map[string]any{
"type": "string",
"minLength": 1,
"description": "A natural-language description of the GraphQL operation to generate.",

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 believe this may need a more descriptive instruction for how to format the prompt, tbd

@fiam
fiam force-pushed the alberto/router-575-router-mcp-query-generation-support branch from ee3a30e to 665d0b9 Compare August 17, 2026 21:44
Replace the functional options with an explicit logger constructor parameter and keep the fixed client timeout internal. Move the package under router/internal since it is only consumed by router core.
@fiam
fiam force-pushed the alberto/router-575-router-mcp-query-generation-support branch from 665d0b9 to b7f1b0f Compare August 17, 2026 21:45
Move bearer authentication and retry configuration into a shared internal transport. Inject it into prompt-to-query from router setup.
@fiam
fiam force-pushed the alberto/router-575-router-mcp-query-generation-support branch from 94b0d9a to 561f7e2 Compare August 17, 2026 22:32
Ensure the schema index exists before generating a query and poll Yoko until the index is ready. Propagate request cancellation through indexing and generation so polling stops when the caller disconnects or reaches its deadline.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants