Skip to content

Commit ff2eebe

Browse files
authored
Add tool filtering to thv serve API. (#1168)
This change adds support for `"tools"` key in the workload creation payload. Fixes #1023
1 parent d31feb5 commit ff2eebe

File tree

8 files changed

+50
-8
lines changed

8 files changed

+50
-8
lines changed

docs/server/docs.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/server/swagger.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/server/swagger.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/api/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func Serve(
217217
return err
218218
}
219219

220-
logger.Infof("starting %s server", addrType, address)
220+
logger.Infof("starting %s server at %s", addrType, address)
221221

222222
// Start server.
223223
go func() {

pkg/api/v1/workloads.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ func (s *WorkloadRoutes) createWorkload(w http.ResponseWriter, r *http.Request)
265265
WithOIDCConfig(req.OIDC.Issuer, req.OIDC.Audience, req.OIDC.JwksURL, req.OIDC.ClientID, req.OIDC.AllowOpaqueTokens,
266266
"", "", false). // JWKS auth parameters not exposed through API yet
267267
WithTelemetryConfig("", false, "", 0.0, nil, false, nil). // Not exposed through API yet.
268+
WithToolsFilter(req.ToolsFilter).
268269
Build(ctx, imageMetadata, req.EnvVars, &runner.DetachedEnvVarValidator{})
269270
if err != nil {
270271
logger.Errorf("Failed to create run config: %v", err)
@@ -485,6 +486,8 @@ type createRequest struct {
485486
ProxyMode string `json:"proxy_mode"`
486487
// Whether network isolation is turned on. This applies the rules in the permission profile.
487488
NetworkIsolation bool `json:"network_isolation"`
489+
// Tools filter
490+
ToolsFilter []string `json:"tools"`
488491
}
489492

490493
// oidcOptions represents OIDC configuration options

pkg/workloads/manager.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -433,18 +433,18 @@ func (d *defaultManager) RunWorkloadDetached(ctx context.Context, runConfig *run
433433
detachedArgs = append(detachedArgs, "--enable-audit")
434434
}
435435

436+
if runConfig.ToolsFilter != nil {
437+
toolsFilter := strings.Join(runConfig.ToolsFilter, ",")
438+
detachedArgs = append(detachedArgs, "--tools", toolsFilter)
439+
}
440+
436441
// Add the image and any arguments
437442
detachedArgs = append(detachedArgs, runConfig.Image)
438443
if len(runConfig.CmdArgs) > 0 {
439444
detachedArgs = append(detachedArgs, "--")
440445
detachedArgs = append(detachedArgs, runConfig.CmdArgs...)
441446
}
442447

443-
if runConfig.ToolsFilter != nil {
444-
toolsFilter := strings.Join(runConfig.ToolsFilter, ",")
445-
detachedArgs = append(detachedArgs, "--tools", toolsFilter)
446-
}
447-
448448
// Create a new command
449449
// #nosec G204 - This is safe as execPath is the path to the current binary
450450
detachedCmd := exec.Command(execPath, detachedArgs...)

pkg/workloads/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ type Workload struct {
6666
Labels map[string]string `json:"labels,omitempty"`
6767
// Group is the name of the group this workload belongs to, if any.
6868
Group string `json:"group,omitempty"`
69+
// ToolsFilter is the filter on tools applied to the workload.
70+
ToolsFilter []string `json:"tools,omitempty"`
6971
}
7072

7173
// loadGroupFromRunConfig attempts to load group information from the runconfig

test/e2e/fetch_mcp_server_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,31 @@ var _ = Describe("FetchMcpServer", func() {
8585
})
8686
})
8787

88+
Context("when starting the server from registry with tools filter", func() {
89+
It("should start when filters are correct", func() {
90+
By("Starting the fetch MCP server")
91+
stdout, stderr := e2e.NewTHVCommand(config, "run", "--name", serverName, "fetch", "--tools", "fetch").ExpectSuccess()
92+
93+
// The command should indicate success
94+
Expect(stdout+stderr).To(ContainSubstring("fetch"), "Output should mention the fetch server")
95+
96+
By("Waiting for the server to be running")
97+
err := e2e.WaitForMCPServer(config, serverName, 60*time.Second)
98+
Expect(err).ToNot(HaveOccurred(), "Server should be running within 30 seconds")
99+
100+
By("Verifying the server appears in the list")
101+
stdout, _ = e2e.NewTHVCommand(config, "list").ExpectSuccess()
102+
Expect(stdout).To(ContainSubstring(serverName), "Server should appear in the list")
103+
Expect(stdout).To(ContainSubstring("running"), "Server should be in running state")
104+
})
105+
106+
It("should not start when filters are incorrect", func() {
107+
By("Starting the fetch MCP server")
108+
_, _, err := e2e.NewTHVCommand(config, "run", "--name", serverName, "fetch", "--tools", "wrong-tool").ExpectFailure()
109+
Expect(err).To(HaveOccurred(), "Should fail with non-existent server")
110+
})
111+
})
112+
88113
Context("when managing the server lifecycle", func() {
89114
BeforeEach(func() {
90115
// Start a server for lifecycle tests

0 commit comments

Comments
 (0)