Skip to content

Comments

mcp server for ml feature ops#338

Open
a0d00kc wants to merge 1 commit intodevelopfrom
feat/on-fs-mcp
Open

mcp server for ml feature ops#338
a0d00kc wants to merge 1 commit intodevelopfrom
feat/on-fs-mcp

Conversation

@a0d00kc
Copy link
Contributor

@a0d00kc a0d00kc commented Feb 16, 2026

🔁 Pull Request Template – BharatMLStack

Please fill out the following sections to help us review your changes efficiently.

Context:

Give a brief overview of the motivation behind this change. Include any relevant discussion links (Slack, documents, tickets, etc.) that help reviewers understand the background and the issue being addressed.

Describe your changes:

Mention the changes made in the codebase.

Testing:

Please describe how you tested the code. If manual tests were performed - please explain how. If automatic tests were added or existing ones cover the change - please explain how did you run them.

Monitoring:

Explain how this change will be tracked after deployment. Indicate whether current dashboards, alerts, and logs are enough, or if additional instrumentation is required.

Rollback plan

Explain rollback plan in case of issues.

Checklist before requesting a review

  • I have reviewed my own changes?
  • Relevant or critical functionality is covered by tests?
  • Monitoring needs have been evaluated?
  • Any necessary documentation updates have been considered?

📂 Modules Affected

  • horizon (Real-time systems / networking)
  • online-feature-store (Feature serving infra)
  • trufflebox-ui (Admin panel / UI)
  • infra (Docker, CI/CD, GCP/AWS setup)
  • docs (Documentation updates)
  • Other: ___________

✅ Type of Change

  • Feature addition
  • Bug fix
  • Infra / build system change
  • Performance improvement
  • Refactor
  • Documentation
  • Other: ___________

📊 Benchmark / Metrics (if applicable)

Summary by CodeRabbit

  • New Features
    • Added MCP (Model Context Protocol) server support to both Horizon and Online-Feature-Store for external tool integration.
    • Horizon: Exposed entity discovery and feature group exploration tools.
    • Online-Feature-Store: Added feature retrieval and health check tools via MCP.
    • Configured MCP server with optional TLS support via environment variables.

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

Walkthrough

This pull request introduces MCP (Model Context Protocol) server integration to both the Horizon and Online Feature Store projects. New server entrypoints are added for each module with tool handlers enabling entity/feature discovery and retrieval capabilities via the MCP protocol. Dependencies on the mcp-go library are introduced.

Changes

Cohort / File(s) Summary
Horizon MCP Server Entrypoint
horizon/cmd/mcp-server/main.go, horizon/go.mod
Adds new MCP server entrypoint with config initialization, logging, database connector setup, feature store initialization, and HTTP server startup with optional TLS support. Introduces github.com/mark3labs/mcp-go v0.44.0 dependency and transitive dependencies for JSON schema and utilities.
Horizon MCP Tools
horizon/internal/mcp/server.go, horizon/internal/mcp/tools.go
Implements MCP server constructor and four discovery tools: list_entities, get_entity_details, list_feature_groups, and get_feature_group_details. Tool handlers delegate to config handlers for entity/feature group retrieval and JSON serialization with error handling.
Online Feature Store MCP Server Entrypoint
online-feature-store/cmd/mcp-server/main.go, online-feature-store/go.mod
Adds new MCP server entrypoint with environment setup, etcd config manager initialization, watch-path callbacks registration, feature handlers initialization, and HTTP server startup with optional TLS configuration. Introduces github.com/mark3labs/mcp-go v0.44.0 dependency and transitive dependencies.
Online Feature Store MCP Tools
online-feature-store/internal/mcp/server.go, online-feature-store/internal/mcp/tools.go
Implements MCP server constructor and two feature service tools: retrieve_decoded_features (parses arguments, injects auth metadata, invokes feature service) and health_check (simple status endpoint). Tool handlers manage gRPC context injection and response marshalling with error handling.

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@horizon/internal/mcp/tools.go`:
- Around line 69-77: The summaries slice is left nil when featureGroups is
nil/empty which causes json.Marshal(summaries) to emit "null" instead of "[]";
initialize summaries as an empty slice (e.g., make([]fgSummary, 0) or
[]fgSummary{}) before appending so that the variable used later (summaries)
always marshals to an empty JSON array; update the block around the summaries
declaration and the loop that iterates over featureGroups (referencing the
summaries variable and fgSummary type) to ensure summaries is non-nil even when
no featureGroups are present.

In `@online-feature-store/cmd/mcp-server/main.go`:
- Around line 7-8: Remove the duplicate import of the same package
(github.com/Meesho/BharatMLStack/online-feature-store/internal/config) so only
one alias is used (keep featureConfig and remove config), then update any usages
that reference config (e.g., in main where config.SomeVar or config.NewX is
used) to use featureConfig instead so all references consistently use
featureConfig.

In `@online-feature-store/internal/mcp/tools.go`:
- Around line 170-182: injectAuthContext silently returns the original context
when callerIDEnvVar or authTokenEnvVar are empty, allowing auth bypass; update
injectAuthContext to emit a warning via the existing logger when either env var
is missing (include which one) and continue, or better: add a startup validation
in main() that checks callerIDEnvVar and authTokenEnvVar and fails fast with a
clear error if they are required; reference callerIDEnvVar, authTokenEnvVar,
callerIDHeader, authTokenHeader and metadata.NewIncomingContext to locate the
code and either add logging inside injectAuthContext or perform validation in
main() and exit on missing credentials.
🧹 Nitpick comments (3)
online-feature-store/cmd/mcp-server/main.go (1)

38-48: Consider whether failed watch-path registrations should be fatal.

If etcd watch callbacks fail to register, the server will run with stale configuration and no mechanism to detect it. For circuit breaker and client registration updates, this could be a silent reliability risk. Consider making these fatal or at minimum emitting a metric for alerting.

online-feature-store/internal/mcp/tools.go (1)

163-166: HealthCheck doesn't actually verify service health.

This always returns {"status":"ok"} regardless of the actual state of the feature store. Consider at minimum pinging the underlying handler or a dependency to provide meaningful health status.

horizon/cmd/mcp-server/main.go (1)

42-42: Replace magic number 1 with a named constant.

ofsHandler.NewConfigHandler(1) uses a magic number. Extract this to a descriptive constant (e.g., configHandlerVersion) for clarity.

Proposed fix
+const configHandlerVersion = 1
+
 func main() {
 	var appConfig AppConfig
 	...
-	configHandler := ofsHandler.NewConfigHandler(1)
+	configHandler := ofsHandler.NewConfigHandler(configHandlerVersion)

Comment on lines +69 to +77
var summaries []fgSummary
if featureGroups != nil {
for _, fg := range *featureGroups {
summaries = append(summaries, fgSummary{
Label: fg.FeatureGroupLabel,
DataType: string(fg.DataType),
})
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

json.Marshal(nil) produces "null", not "[]" — initialize the slice.

When featureGroups is nil or empty, summaries stays nil, and json.Marshal(nil) outputs null. MCP tool consumers likely expect an empty JSON array.

Proposed fix
-	var summaries []fgSummary
-	if featureGroups != nil {
+	summaries := make([]fgSummary, 0)
+	if featureGroups != nil {
🤖 Prompt for AI Agents
In `@horizon/internal/mcp/tools.go` around lines 69 - 77, The summaries slice is
left nil when featureGroups is nil/empty which causes json.Marshal(summaries) to
emit "null" instead of "[]"; initialize summaries as an empty slice (e.g.,
make([]fgSummary, 0) or []fgSummary{}) before appending so that the variable
used later (summaries) always marshals to an empty JSON array; update the block
around the summaries declaration and the loop that iterates over featureGroups
(referencing the summaries variable and fgSummary type) to ensure summaries is
non-nil even when no featureGroups are present.

Comment on lines +7 to +8
"github.com/Meesho/BharatMLStack/online-feature-store/internal/config"
featureConfig "github.com/Meesho/BharatMLStack/online-feature-store/internal/config"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix duplicate import — CI is failing (ST1019).

Both config and featureConfig alias the same package github.com/Meesho/BharatMLStack/online-feature-store/internal/config. Remove one and use a single alias throughout.

Proposed fix
-	"github.com/Meesho/BharatMLStack/online-feature-store/internal/config"
-	featureConfig "github.com/Meesho/BharatMLStack/online-feature-store/internal/config"
+	featureConfig "github.com/Meesho/BharatMLStack/online-feature-store/internal/config"

Then update line 30 to use featureConfig instead of config:

-	etcd.Init(configManagerVersion, &config.FeatureRegistry{})
+	etcd.Init(configManagerVersion, &featureConfig.FeatureRegistry{})
🧰 Tools
🪛 GitHub Actions: Online Feature Store CI

[error] 7-7: staticcheck: package "github.com/Meesho/BharatMLStack/online-feature-store/internal/config" is being imported more than once (ST1019).

🤖 Prompt for AI Agents
In `@online-feature-store/cmd/mcp-server/main.go` around lines 7 - 8, Remove the
duplicate import of the same package
(github.com/Meesho/BharatMLStack/online-feature-store/internal/config) so only
one alias is used (keep featureConfig and remove config), then update any usages
that reference config (e.g., in main where config.SomeVar or config.NewX is
used) to use featureConfig instead so all references consistently use
featureConfig.

Comment on lines +170 to +182
func injectAuthContext(ctx context.Context) context.Context {
callerID := os.Getenv(callerIDEnvVar)
authToken := os.Getenv(authTokenEnvVar)

if callerID == "" || authToken == "" {
return ctx
}

md := metadata.New(map[string]string{
callerIDHeader: callerID,
authTokenHeader: authToken,
})
return metadata.NewIncomingContext(ctx, md)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Silent auth bypass when env vars are unset.

If OFS_CALLER_ID or OFS_AUTH_TOKEN are not configured, injectAuthContext silently returns the original context without auth metadata. The downstream gRPC handler may or may not reject unauthenticated requests. Consider logging a warning or failing at startup if these are expected to be set.

Option: warn on missing credentials
 func injectAuthContext(ctx context.Context) context.Context {
 	callerID := os.Getenv(callerIDEnvVar)
 	authToken := os.Getenv(authTokenEnvVar)
 
 	if callerID == "" || authToken == "" {
+		// Consider logging a warning here or validating at startup
 		return ctx
 	}

Alternatively, validate these env vars in main() at startup and fail fast.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func injectAuthContext(ctx context.Context) context.Context {
callerID := os.Getenv(callerIDEnvVar)
authToken := os.Getenv(authTokenEnvVar)
if callerID == "" || authToken == "" {
return ctx
}
md := metadata.New(map[string]string{
callerIDHeader: callerID,
authTokenHeader: authToken,
})
return metadata.NewIncomingContext(ctx, md)
func injectAuthContext(ctx context.Context) context.Context {
callerID := os.Getenv(callerIDEnvVar)
authToken := os.Getenv(authTokenEnvVar)
if callerID == "" || authToken == "" {
// Consider logging a warning here or validating at startup
return ctx
}
md := metadata.New(map[string]string{
callerIDHeader: callerID,
authTokenHeader: authToken,
})
return metadata.NewIncomingContext(ctx, md)
}
🤖 Prompt for AI Agents
In `@online-feature-store/internal/mcp/tools.go` around lines 170 - 182,
injectAuthContext silently returns the original context when callerIDEnvVar or
authTokenEnvVar are empty, allowing auth bypass; update injectAuthContext to
emit a warning via the existing logger when either env var is missing (include
which one) and continue, or better: add a startup validation in main() that
checks callerIDEnvVar and authTokenEnvVar and fails fast with a clear error if
they are required; reference callerIDEnvVar, authTokenEnvVar, callerIDHeader,
authTokenHeader and metadata.NewIncomingContext to locate the code and either
add logging inside injectAuthContext or perform validation in main() and exit on
missing credentials.

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.

1 participant