Skip to content

Conversation

@blublinsky
Copy link
Contributor

@blublinsky blublinsky commented Jan 20, 2026

Description

Type of change

  • [ x] Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change

Related Tickets & Documents

Checklist before requesting a review

  • I have performed a self-review of my code.
  • PR has passed all pre-merge test jobs.
  • If it is a core feature, I have added thorough tests.

Testing

  • Please provide detailed steps to perform tests related to this code change.
  • How were the fix/results from this change verified? Please provide relevant screenshots or results.

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 20, 2026
@openshift-ci openshift-ci bot requested review from raptorsun and tisnik January 20, 2026 17:10
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 20, 2026
@blublinsky blublinsky force-pushed the mcp-simplification branch 3 times, most recently from 19fcc14 to 65d866f Compare January 21, 2026 14:59
@blublinsky blublinsky changed the title removed additional mcp transport, added MCP headers to query, streaml… removed additional mcp transport, streamlined MCP Auth resolution Jan 21, 2026
@blublinsky
Copy link
Contributor Author

/retest

timeout: int = constants.SSE_TRANSPORT_DEFAULT_TIMEOUT
sse_read_timeout: int = constants.SSE_TRANSPORT_DEFAULT_READ_TIMEOUT
headers: dict[str, str] = Field(default_factory=dict)
authorization_headers: dict[str, str] = Field(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we rename this to just headers? I understand we use it for auth purposes now, but it can be any other arbitrary headers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can, but this was original name. To do this, we also need to change operator. So unless you absolutely want this, I would prefer to leave it

Copy link
Contributor

Choose a reason for hiding this comment

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

Where? I see you changed it in one of the lcore func in operator, but this service accepts only headers in the current head.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in the operator internal/controller/appserver/assets.go, line 783

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if that is the right reference: https://github.com/openshift/lightspeed-operator/blob/main/internal/controller/appserver/assets.go#L783C7-L783C122
Currently it produces this config:

mcp_servers:
  - name: "openshift"
    transport: "streamable_http"
    streamable_http:
      url: "http://localhost:8080/mcp"
      timeout: 60
      sse_read_timeout: 30
      headers:
        Authorization: "kubernetes"

The authorization_headers is used for lcore config.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

"""

name: str = Field(
...,
Copy link
Contributor

Choose a reason for hiding this comment

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

The Field should work without the Ellipsis, eg. just

name: str = Field(
    title="MCP name",
    description="MCP server name that must be unique",
)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is required field. Thats Pydantic convention to use Elipsees

Copy link
Contributor

Choose a reason for hiding this comment

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

It is required for pydantic < 2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

"""Resolved authorization headers (computed from authorization_headers)."""
return self._resolved_authorization_headers

timeout: Optional[int] = Field(
Copy link
Contributor

Choose a reason for hiding this comment

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

Please move up to the rest of the class attributes - before the private attr and property

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

sse: Optional[SseTransportConfig] = None
streamable_http: Optional[StreamableHttpTransportConfig] = None
embed_model: str = Field(
default="sentence-transformers/all-mpnet-base-v2",
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you tested other (smaller) models? Or we just use the same thing we already have available?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

both

sse_read_timeout: int = constants.STREAMABLE_HTTP_TRANSPORT_DEFAULT_READ_TIMEOUT
headers: dict[str, str] = Field(default_factory=dict)

class ToolFilteringConfig(BaseModel):
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't need to be in this PR, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am synching operator with service. Its question of 4 PRs vs 2

Copy link
Contributor

Choose a reason for hiding this comment

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

Are you confident that this config won't change with the actual implementation of the tool filtering? It would make sense to me to have this as part of the actual implementation, but if you tell me this is stable lets proceed.


proxy_config: Optional[ProxyConfig] = None

tool_filtering: Optional[ToolFilteringConfig] = None
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it a feature of mcp servers or ols? Answer implies position in the config.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ols - tools filtering is an OLS property

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, the lines were truncated in a way that it seems like it is under MCPConfig, my bad.

resolved: dict[str, str] = {}

for header_name, header_value in authorization_headers.items():
match header_value.strip():
Copy link
Contributor

Choose a reason for hiding this comment

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

Should it also be case-insensitive for special values "kubernetes" and "client"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Its using constants now

Copy link
Contributor

Choose a reason for hiding this comment

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

Would both of these work the same?

mcp_servers:
- name: openshift
  url: "http://localhost:8989/mcp"
  headers:
     bla: "kubernetes"

and

mcp_servers:
- name: openshift
  url: "http://localhost:8989/mcp"
  headers:
     bla: "KUBERNETES"

@blublinsky blublinsky force-pushed the mcp-simplification branch 2 times, most recently from 0209b07 to 6ec8fa5 Compare January 22, 2026 13:19
@xrajesh xrajesh requested a review from asamal4 January 22, 2026 15:53
"config should not be provided"
)
return self
embed_model: str = Field(
Copy link
Contributor

@asamal4 asamal4 Jan 23, 2026

Choose a reason for hiding this comment

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

Probably this should be a comment for operator changes.
is this going to be user facing ? We will probably end up using same embedding model what we package as part of RAG, considering on-prem scenario.

As far service is concerned, It is not aligned with the RAG embedding model property. This is in-consistent

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a completely separate RAG. So technically here we can use a completely different model. In general I do not foresee users ever changing it, but just in case it is there. And of course it is aligned with the operator

Copy link
Contributor

@asamal4 asamal4 Jan 23, 2026

Choose a reason for hiding this comment

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

I am specifically talking about on-prem scenario where we can not download the model from huggingface. Model has to be picked from file-system/volume. For RAG use-case we are handling this. So similar processing we need to do for tool rag also. In this case we will provide a path to the embedding model..

Copy link
Contributor

@asamal4 asamal4 Jan 23, 2026

Choose a reason for hiding this comment

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

Regarding parameter name inconsistency in Service/API: Agree that this embedding model is for different purpose (and can be a different model), but IMO we should keep same parameter name in both places. For RAG the name is embeddings_model_path and here we are using embed_model. But this is not very critical. It's up to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@blublinsky blublinsky force-pushed the mcp-simplification branch 2 times, most recently from d670d7d to 012bebe Compare January 23, 2026 15:29
@blublinsky
Copy link
Contributor Author

/retest

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 23, 2026
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 23, 2026
@blublinsky
Copy link
Contributor Author

ci/prow/e2e-ols-cluster failure is expected for the following tests:
azure_openai_tool_calling
openai_tool_calling
watsonx_tool_calling
These tests add MCP configuration, but the operator is still creating old configuration. As a result app server fails to start properly

url: http://api-service:8080
authorization_headers:
Authorization: /var/secrets/api-token # Path to file containing token
X-API-Key: /var/secrets/api-key # Multiple headers supported
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, but maybe make this look more like a traditional header name?


#### 2. Kubernetes Token (User Context)

Use the special `kubernetes` placeholder to automatically inject the authenticated user's Kubernetes token. This requires the `k8s` authentication module:
Copy link
Contributor

Choose a reason for hiding this comment

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

describe what header(name, format) is passed to the MCP server when this is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can apply to any header name. If you are asking that it has to be Authorization, the answer is no

Copy link
Contributor

Choose a reason for hiding this comment

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

So in the sample:

authorization_headers:
      Authorization: kubernetes

Authorization is the name of the header that will be passed to the mcp server? So can i write:

authorization_headers:
      my-custom-header: kubernetes

?

If so, there is a lot of implicit behavior here that I can't say i love.

Explicit behavior would be something like:

authorization_headers:
  k8s-token:
    headername: Authorization
  custom-header:
   headername: my-custom-header
   headervalue: /path/to/secret

(And if either of those subsections aren't populated, that behavior isn't enabled)

but at a minimum if you're going to keep it this way, more doc is needed please

Copy link
Contributor Author

Choose a reason for hiding this comment

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

header_name: kubernetes token


#### 2. Kubernetes Token (User Context)

Use the special `kubernetes` placeholder to automatically inject the authenticated user's Kubernetes token. This requires the `k8s` authentication module:
Copy link
Contributor

Choose a reason for hiding this comment

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

So in the sample:

authorization_headers:
      Authorization: kubernetes

Authorization is the name of the header that will be passed to the mcp server? So can i write:

authorization_headers:
      my-custom-header: kubernetes

?

If so, there is a lot of implicit behavior here that I can't say i love.

Explicit behavior would be something like:

authorization_headers:
  k8s-token:
    headername: Authorization
  custom-header:
   headername: my-custom-header
   headervalue: /path/to/secret

(And if either of those subsections aren't populated, that behavior isn't enabled)

but at a minimum if you're going to keep it this way, more doc is needed please

url: http://api-service:8080
authorization_headers:
Authorization: /var/secrets/api-token # Path to file containing token
X-API-Key: /var/secrets/api-key # Multiple headers supported
Copy link
Contributor

Choose a reason for hiding this comment

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

also how do multiple headers work? How do you specify more than one, and does each one have a different path to a file containing its token? how are they linked?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

you can specify as many as you want, each with its own path. they are all added as headers to the MCP server calls

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah but how do you specify more than one? This example seems to imply it's not a yaml array, so what's the actual schema to do that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah. you are looking at the service, that is pure python executable driven by configuration. Yaml is used by the operator openshift/lightspeed-operator#1222

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah. you are looking at the service, that knows nothing about yaml. jaml is in the operator.

@openshift-ci
Copy link

openshift-ci bot commented Jan 26, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign joshuawilson for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Copy link
Contributor

@onmete onmete left a comment

Choose a reason for hiding this comment

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

Lets resolve pending comments and proceed with this.

),
)

headers: dict[str, str] = Field(
Copy link
Contributor

Choose a reason for hiding this comment

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

So you kept it as headers, right? Can you please update the title and rest of the refs (docs) that are using authorization_headers?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

- name: github
url: http://github-mcp-server:8080
headers:
Authorization: client # Client provides token via MCP-HEADERS header
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you pretty please explain why we need to bring this to the OLS before we migrate to LCORE? Do you see a usecase/need prior to that?

Copy link
Contributor Author

@blublinsky blublinsky Jan 27, 2026

Choose a reason for hiding this comment

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

because

  1. We are making a change in the operator, which is not compatible with the old configuration. Its easier to make a change here to keep an operator clean
  2. This makes the code smaller and more streamlined
  3. It makes it simpler to wire tools RAG, next PRs
  4. There is an important corner case in toolsRag implementation related to the client's headers. I would like to make sure I do it correctly here before moving it to LCore

Copy link
Contributor

Choose a reason for hiding this comment

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

I have my reservations here, but for the sake of progressing, let's move on.

@onmete
Copy link
Contributor

onmete commented Jan 27, 2026

So in the sample:

authorization_headers:
Authorization: kubernetes
Authorization is the name of the header that will be passed to the mcp server? So can i write:

authorization_headers:
my-custom-header: kubernetes
?

If so, there is a lot of implicit behavior here that I can't say i love.

Explicit behavior would be something like:

authorization_headers:
k8s-token:
headername: Authorization
custom-header:
headername: my-custom-header
headervalue: /path/to/secret
(And if either of those subsections aren't populated, that behavior isn't enabled)

but at a minimum if you're going to keep it this way, more doc is needed please

Why I can't just reply and continue the conversation as a thread here - github ux is driving me nuts...

Anyway, @bparees I agree that this would be more explicit for users. We can consider using this format in the user-facing OLSConfig CR. But changing this on the config level would require to also change it in LCORE side, which might (or might not) break someone else on LCORE that already uses it.
WDYT @blublinsky?

@blublinsky
Copy link
Contributor Author

the user facing definition openshift/lightspeed-operator#1222 describes it clearer (I hope) and even contains examples

@openshift-ci
Copy link

openshift-ci bot commented Jan 29, 2026

@blublinsky: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-ols-cluster 38ab1d6 link true /test e2e-ols-cluster
ci/prow/ols-evaluation 38ab1d6 link true /test ols-evaluation

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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.

5 participants