Files
multica/server/internal/handler/plugin_test.go
T
4d495056e8 [MUL-6139] Expand Plugin skills and hosted MCP connections (#6920)
* feat(plugins): add remote MCP runtime

Co-authored-by: multica-agent <[email protected]>

* chore(plugins): add private Exa example

Co-authored-by: multica-agent <[email protected]>

* docs(plugins): remove private plugin guide

Co-authored-by: multica-agent <[email protected]>

* fix(plugins): stabilize remote MCP tool digests

Co-authored-by: multica-agent <[email protected]>

* fix(plugins): preserve remote MCP notification status

Co-authored-by: multica-agent <[email protected]>

* fix(plugins): make Exa example runnable end to end

Co-authored-by: multica-agent <[email protected]>

* fix(codex): record MCP tool calls in agent log

Co-authored-by: multica-agent <[email protected]>

* chore(plugins): add community skills example

Co-authored-by: multica-agent <[email protected]>

* feat(plugins): support multi-file skill bundles

Co-authored-by: multica-agent <[email protected]>

* feat(plugins): add Mobbin MCP example

Co-authored-by: multica-agent <[email protected]>

* chore(plugins): bundle full Matt Pocock skill set

Co-authored-by: multica-agent <[email protected]>

* feat(plugins): add remote MCP OAuth connections

Co-authored-by: multica-agent <[email protected]>

* fix(plugins): gate enable on remote MCP readiness

Co-authored-by: multica-agent <[email protected]>

* fix(plugin): authenticate remote mcp credential broker

Co-authored-by: multica-agent <[email protected]>

* fix(plugins): harden remote mcp oauth flow

Co-authored-by: multica-agent <[email protected]>

* refactor(plugins): use a single feature flag

Co-authored-by: multica-agent <[email protected]>

* fix(plugins): clean up remote MCP OAuth state

Co-authored-by: multica-agent <[email protected]>

* chore(plugins): remove Matt Pocock example

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Lambda <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-08-16 04:54:32 +08:00

447 lines
20 KiB
Go

package handler
import (
"bytes"
"context"
"encoding/json"
"errors"
"mime/multipart"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
"github.com/go-chi/chi/v5"
"github.com/jackc/pgx/v5"
"github.com/multica-ai/multica/server/internal/testutil/plugintest"
db "github.com/multica-ai/multica/server/pkg/db/generated"
"github.com/multica-ai/multica/server/pkg/plugincontract"
"github.com/multica-ai/multica/server/pkg/pluginruntime"
)
type failPluginDetailQueryDB struct {
db.DBTX
queryFragment string
queryRowFragment string
failQueryRowAtMatch int
queryRowMatches int
}
func (database *failPluginDetailQueryDB) Query(ctx context.Context, query string, args ...any) (pgx.Rows, error) {
if database.queryFragment != "" && strings.Contains(query, database.queryFragment) {
return nil, errors.New("forced Plugin detail query failure")
}
return database.DBTX.Query(ctx, query, args...)
}
type pluginDetailErrorRow struct{}
func (pluginDetailErrorRow) Scan(...any) error {
return errors.New("forced Plugin detail query failure")
}
func (database *failPluginDetailQueryDB) QueryRow(ctx context.Context, query string, args ...any) pgx.Row {
if database.queryRowFragment != "" && strings.Contains(query, database.queryRowFragment) {
database.queryRowMatches++
if database.queryRowMatches == database.failQueryRowAtMatch {
return pluginDetailErrorRow{}
}
}
return database.DBTX.QueryRow(ctx, query, args...)
}
func pluginHandlerRequest(method, path string, body []byte, params map[string]string) *http.Request {
request := httptest.NewRequest(method, path, bytes.NewReader(body))
request.Header.Set("X-User-ID", testUserID)
request.Header.Set("Content-Type", "application/json")
routeContext := chi.NewRouteContext()
for key, value := range params {
routeContext.URLParams.Add(key, value)
}
return request.WithContext(context.WithValue(request.Context(), chi.RouteCtxKey, routeContext))
}
func TestRemoteMCPConfigResponseReady(t *testing.T) {
ready := pluginRemoteMCPConfigResponse{
ConfigRevision: 1, CredentialState: "configured", Reviewed: true,
ApprovedTools: []pluginruntime.RemoteMCPTool{{Name: "search"}}, SchemaDigest: "sha256:test",
}
if !remoteMCPConfigResponseReady(ready) {
t.Fatal("complete Remote MCP configuration was not ready")
}
for name, edit := range map[string]func(*pluginRemoteMCPConfigResponse){
"missing config": func(config *pluginRemoteMCPConfigResponse) { config.ConfigRevision = 0 },
"missing credential": func(config *pluginRemoteMCPConfigResponse) { config.CredentialState = "missing" },
"unreviewed": func(config *pluginRemoteMCPConfigResponse) { config.Reviewed = false },
"no approved tools": func(config *pluginRemoteMCPConfigResponse) { config.ApprovedTools = nil },
"missing digest": func(config *pluginRemoteMCPConfigResponse) { config.SchemaDigest = "" },
} {
t.Run(name, func(t *testing.T) {
config := ready
edit(&config)
if remoteMCPConfigResponseReady(config) {
t.Fatal("incomplete Remote MCP configuration was ready")
}
})
}
}
func TestPluginHTTPLifecycleForInstalledReferenceRelease(t *testing.T) {
withPluginsV1Flag(t, testHandler, true)
cleanup := func() {
ctx := context.Background()
testPool.Exec(ctx, `DELETE FROM plugin_health WHERE workspace_id = $1`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_capability_snapshot WHERE workspace_id = $1`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_workspace_capability_state WHERE workspace_id = $1`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_binding WHERE installation_id IN (SELECT id FROM plugin_installation WHERE workspace_id = $1)`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_grant WHERE installation_id IN (SELECT id FROM plugin_installation WHERE workspace_id = $1)`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_installation WHERE workspace_id = $1`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM activity_log WHERE workspace_id = $1 AND action = 'plugin_uninstalled'`, testWorkspaceID)
}
cleanup()
t.Cleanup(cleanup)
recorder := httptest.NewRecorder()
testHandler.ListPluginCatalog(recorder, pluginHandlerRequest(http.MethodGet, "/plugins/catalog", nil, map[string]string{"id": testWorkspaceID}))
if recorder.Code != http.StatusOK || !bytes.Contains(recorder.Body.Bytes(), []byte(`"signature_verified":true`)) || !bytes.Contains(recorder.Body.Bytes(), []byte(plugintest.ReviewReadinessPluginKey)) {
t.Fatalf("catalog status=%d body=%s", recorder.Code, recorder.Body.String())
}
var catalogPayload struct {
Diagnostics json.RawMessage `json:"diagnostics"`
}
if err := json.Unmarshal(recorder.Body.Bytes(), &catalogPayload); err != nil {
t.Fatalf("decode catalog: %v", err)
}
if !bytes.Equal(catalogPayload.Diagnostics, []byte(`[]`)) {
t.Fatalf("empty catalog diagnostics must be an array, got %s", catalogPayload.Diagnostics)
}
recorder = httptest.NewRecorder()
testHandler.InstallPlugin(recorder, pluginHandlerRequest(
http.MethodPost,
"/plugins/install",
[]byte(`{"plugin_key":"ai.multica.software-delivery","version":"1.0.0"}`),
map[string]string{"id": testWorkspaceID},
))
if recorder.Code != http.StatusCreated {
t.Fatalf("install status=%d body=%s", recorder.Code, recorder.Body.String())
}
var installed pluginInstallationResponse
if err := json.Unmarshal(recorder.Body.Bytes(), &installed); err != nil {
t.Fatalf("decode installation: %v", err)
}
if installed.Enabled || installed.LifecycleStatus != "installed" {
t.Fatalf("install must remain disabled: %+v", installed)
}
params := map[string]string{"id": testWorkspaceID, "installationId": installed.ID}
recorder = httptest.NewRecorder()
testHandler.EnablePlugin(recorder, pluginHandlerRequest(
http.MethodPost,
"/plugins/enable",
[]byte(`{"scope_type":"workspace","scope_id":"00000000-0000-0000-0000-000000000001"}`),
params,
))
if recorder.Code != http.StatusNotFound || bytes.Contains(recorder.Body.Bytes(), []byte("no rows")) {
t.Fatalf("cross-workspace binding status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.EnablePlugin(recorder, pluginHandlerRequest(http.MethodPost, "/plugins/enable", nil, params))
if recorder.Code != http.StatusOK {
t.Fatalf("enable status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.ListPlugins(recorder, pluginHandlerRequest(http.MethodGet, "/plugins", nil, map[string]string{"id": testWorkspaceID}))
if recorder.Code != http.StatusOK || !bytes.Contains(recorder.Body.Bytes(), []byte(plugintest.ReviewReadinessPluginKey)) {
t.Fatalf("list status=%d body=%s", recorder.Code, recorder.Body.String())
}
for _, test := range []struct {
name string
queryFragment string
queryRowFragment string
failQueryRowAtMatch int
}{
{name: "contributions", queryFragment: "FROM plugin_contribution"},
{name: "bindings", queryFragment: "FROM plugin_binding"},
{name: "available releases", queryFragment: "FROM plugin_release release"},
{name: "active release", queryRowFragment: "FROM plugin_release\nWHERE id = $1", failQueryRowAtMatch: 2},
} {
t.Run("list fails closed when "+test.name+" cannot be read", func(t *testing.T) {
failingHandler := *testHandler
failingHandler.Queries = db.New(&failPluginDetailQueryDB{
DBTX: testPool,
queryFragment: test.queryFragment,
queryRowFragment: test.queryRowFragment,
failQueryRowAtMatch: test.failQueryRowAtMatch,
})
recorder := httptest.NewRecorder()
failingHandler.ListPlugins(recorder, pluginHandlerRequest(http.MethodGet, "/plugins", nil, map[string]string{"id": testWorkspaceID}))
if recorder.Code != http.StatusInternalServerError {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
})
}
recorder = httptest.NewRecorder()
testHandler.UpgradePlugin(recorder, pluginHandlerRequest(
http.MethodPost,
"/plugins/upgrade",
[]byte(`{"plugin_key":"ai.multica.software-delivery","version":"1.1.0"}`),
params,
))
if recorder.Code != http.StatusOK || !bytes.Contains(recorder.Body.Bytes(), []byte(`"desired_version":"1.1.0"`)) {
t.Fatalf("upgrade status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.UpgradePlugin(recorder, pluginHandlerRequest(
http.MethodPost,
"/plugins/upgrade",
[]byte(`{"plugin_key":"ai.multica.software-delivery","version":"1.0.0"}`),
params,
))
if recorder.Code != http.StatusConflict || !bytes.Contains(recorder.Body.Bytes(), []byte("not newer")) {
t.Fatalf("downgrade through upgrade status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.DisablePlugin(recorder, pluginHandlerRequest(http.MethodPost, "/plugins/disable", nil, params))
if recorder.Code != http.StatusOK {
t.Fatalf("disable status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.RollbackPlugin(recorder, pluginHandlerRequest(http.MethodPost, "/plugins/rollback", []byte(`{"version":"1.0.0"}`), params))
if recorder.Code != http.StatusOK || !bytes.Contains(recorder.Body.Bytes(), []byte(`"desired_version":"1.0.0"`)) {
t.Fatalf("rollback status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.RollbackPlugin(recorder, pluginHandlerRequest(http.MethodPost, "/plugins/rollback", []byte(`{"version":"9.9.9"}`), params))
if recorder.Code != http.StatusNotFound || bytes.Contains(recorder.Body.Bytes(), []byte("no rows")) {
t.Fatalf("safe missing rollback status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.UninstallPlugin(recorder, pluginHandlerRequest(http.MethodDelete, "/plugins/"+installed.ID, nil, params))
if recorder.Code != http.StatusNoContent {
t.Fatalf("official uninstall status=%d body=%s", recorder.Code, recorder.Body.String())
}
var uninstallAuditCount int
if err := testPool.QueryRow(context.Background(), `
SELECT count(*) FROM activity_log
WHERE workspace_id = $1 AND action = 'plugin_uninstalled'
`, testWorkspaceID).Scan(&uninstallAuditCount); err != nil || uninstallAuditCount != 1 {
t.Fatalf("official uninstall audit count=%d err=%v", uninstallAuditCount, err)
}
}
func TestPluginManagementUnavailableWhenFlagDisabled(t *testing.T) {
h := &Handler{}
request := func() *http.Request {
return pluginHandlerRequest(http.MethodGet, "/plugins", nil, map[string]string{
"id": testWorkspaceID,
"installationId": "00000000-0000-0000-0000-000000000001",
"pluginKey": plugintest.ReviewReadinessPluginKey,
"pluginRef": plugintest.ReviewReadinessPluginKey,
})
}
for name, handler := range map[string]http.HandlerFunc{
"list installed": h.ListPlugins,
"list catalog": h.ListPluginCatalog,
"catalog detail": h.GetPluginCatalogRelease,
"install": h.InstallPlugin,
"upgrade": h.UpgradePlugin,
"enable": h.EnablePlugin,
"disable": h.DisablePlugin,
"rollback": h.RollbackPlugin,
"private list": h.ListPrivatePlugins,
"private status": h.GetPrivatePluginStatus,
"private install": h.InstallPrivatePlugin,
"uninstall": h.UninstallPlugin,
"remote oauth start": h.StartPluginRemoteMCPOAuth,
"remote oauth callback": h.CompletePluginRemoteMCPOAuth,
"remote configure": h.ConfigurePluginRemoteMCP,
"remote test": h.TestPluginRemoteMCP,
"remote review": h.ReviewPluginRemoteMCPTools,
"remote revoke": h.RevokePluginRemoteMCPCredential,
} {
t.Run(name, func(t *testing.T) {
recorder := httptest.NewRecorder()
handler(recorder, request())
if recorder.Code != http.StatusServiceUnavailable {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
if bytes.Contains(recorder.Body.Bytes(), []byte(plugintest.ReviewReadinessPluginKey)) {
t.Fatalf("disabled response leaked catalog data: %s", recorder.Body.String())
}
})
}
}
func TestPrivatePluginManagementRequiresPluginsV1(t *testing.T) {
h := &Handler{}
withPluginsV1Flag(t, h, false)
for name, handler := range map[string]http.HandlerFunc{
"list": h.ListPrivatePlugins,
"status": h.GetPrivatePluginStatus,
"install": h.InstallPrivatePlugin,
} {
t.Run(name, func(t *testing.T) {
recorder := httptest.NewRecorder()
handler(recorder, pluginHandlerRequest(http.MethodGet, "/plugins/private", nil, map[string]string{"id": testWorkspaceID}))
if recorder.Code != http.StatusServiceUnavailable {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
})
}
}
func TestPrivatePluginHTTPUploadListAndUninstall(t *testing.T) {
withPluginsV1Flag(t, testHandler, true)
cleanup := func() {
ctx := context.Background()
testPool.Exec(ctx, `DELETE FROM plugin_health WHERE workspace_id = $1`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_capability_snapshot WHERE workspace_id = $1`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_workspace_capability_state WHERE workspace_id = $1`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_binding WHERE installation_id IN (SELECT id FROM plugin_installation WHERE workspace_id = $1)`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_grant WHERE installation_id IN (SELECT id FROM plugin_installation WHERE workspace_id = $1)`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_installation WHERE workspace_id = $1`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_contribution WHERE release_id IN (SELECT id FROM plugin_release WHERE plugin_id IN (SELECT id FROM plugin_identity WHERE owner_workspace_id = $1))`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_artifact_file WHERE release_id IN (SELECT id FROM plugin_release WHERE plugin_id IN (SELECT id FROM plugin_identity WHERE owner_workspace_id = $1))`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_release WHERE plugin_id IN (SELECT id FROM plugin_identity WHERE owner_workspace_id = $1)`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM plugin_identity WHERE owner_workspace_id = $1`, testWorkspaceID)
testPool.Exec(ctx, `DELETE FROM activity_log WHERE workspace_id = $1 AND action LIKE 'plugin_private_%'`, testWorkspaceID)
}
cleanup()
t.Cleanup(cleanup)
archive, _, err := plugincontract.PackDirectory(filepath.Join("..", "..", "..", "examples", "plugins", "incident-triage"))
if err != nil {
t.Fatalf("pack example private Plugin: %v", err)
}
var body bytes.Buffer
writer := multipart.NewWriter(&body)
part, err := writer.CreateFormFile("artifact", "incident-triage.zip")
if err != nil {
t.Fatal(err)
}
if _, err := part.Write(archive); err != nil {
t.Fatal(err)
}
if err := writer.Close(); err != nil {
t.Fatal(err)
}
request := pluginHandlerRequest(http.MethodPost, "/plugins/private/install", body.Bytes(), map[string]string{"id": testWorkspaceID})
request.Header.Set("Content-Type", writer.FormDataContentType())
recorder := httptest.NewRecorder()
testHandler.InstallPrivatePlugin(recorder, request)
if recorder.Code != http.StatusCreated || !bytes.Contains(recorder.Body.Bytes(), []byte(`"trust_tier":"private_dev"`)) || !bytes.Contains(recorder.Body.Bytes(), []byte(`"signature_verified":false`)) {
t.Fatalf("private install status=%d body=%s", recorder.Code, recorder.Body.String())
}
var installed pluginInstallationResponse
if err := json.Unmarshal(recorder.Body.Bytes(), &installed); err != nil {
t.Fatal(err)
}
recorder = httptest.NewRecorder()
testHandler.ListPrivatePlugins(recorder, pluginHandlerRequest(http.MethodGet, "/plugins/private", nil, map[string]string{"id": testWorkspaceID}))
if recorder.Code != http.StatusOK || !bytes.Contains(recorder.Body.Bytes(), []byte("dev.multica.incident-triage")) {
t.Fatalf("private list status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.GetPrivatePluginStatus(recorder, pluginHandlerRequest(http.MethodGet, "/plugins/private/dev.multica.incident-triage", nil, map[string]string{
"id": testWorkspaceID, "pluginRef": "dev.multica.incident-triage",
}))
if recorder.Code != http.StatusOK || !bytes.Contains(recorder.Body.Bytes(), []byte(`"desired_version":"0.1.0"`)) {
t.Fatalf("private status=%d body=%s", recorder.Code, recorder.Body.String())
}
params := map[string]string{"id": testWorkspaceID, "installationId": installed.ID}
recorder = httptest.NewRecorder()
testHandler.EnablePlugin(recorder, pluginHandlerRequest(http.MethodPost, "/plugins/enable", nil, params))
if recorder.Code != http.StatusOK {
t.Fatalf("private enable status=%d body=%s", recorder.Code, recorder.Body.String())
}
withPluginsV1Flag(t, testHandler, false)
recorder = httptest.NewRecorder()
testHandler.ListPlugins(recorder, pluginHandlerRequest(http.MethodGet, "/plugins", nil, map[string]string{"id": testWorkspaceID}))
if recorder.Code != http.StatusServiceUnavailable {
t.Fatalf("Plugin list with plugins_v1 off status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.EnablePlugin(recorder, pluginHandlerRequest(http.MethodPost, "/plugins/enable", nil, params))
if recorder.Code != http.StatusServiceUnavailable {
t.Fatalf("Plugin enable with plugins_v1 off status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.DisablePlugin(recorder, pluginHandlerRequest(http.MethodPost, "/plugins/disable", nil, params))
if recorder.Code != http.StatusServiceUnavailable {
t.Fatalf("Plugin disable with plugins_v1 off status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.UninstallPlugin(recorder, pluginHandlerRequest(http.MethodDelete, "/plugins/"+installed.ID, nil, map[string]string{
"id": testWorkspaceID, "installationId": installed.ID,
}))
if recorder.Code != http.StatusServiceUnavailable {
t.Fatalf("Plugin uninstall with plugins_v1 off status=%d body=%s", recorder.Code, recorder.Body.String())
}
withPluginsV1Flag(t, testHandler, true)
recorder = httptest.NewRecorder()
testHandler.UninstallPlugin(recorder, pluginHandlerRequest(http.MethodDelete, "/plugins/"+installed.ID, nil, map[string]string{
"id": testWorkspaceID, "installationId": installed.ID,
}))
if recorder.Code != http.StatusNoContent {
t.Fatalf("private uninstall status=%d body=%s", recorder.Code, recorder.Body.String())
}
recorder = httptest.NewRecorder()
testHandler.GetPrivatePluginStatus(recorder, pluginHandlerRequest(http.MethodGet, "/plugins/private/dev.multica.incident-triage", nil, map[string]string{
"id": testWorkspaceID, "pluginRef": "dev.multica.incident-triage",
}))
if recorder.Code != http.StatusNotFound {
t.Fatalf("uninstalled private status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestResolveTaskRemoteMCPCredentialRejectsNonDaemonAuth(t *testing.T) {
h := &Handler{}
recorder := httptest.NewRecorder()
request := pluginHandlerRequest(http.MethodGet, "/api/daemon/tasks/task-id/remote-mcp/contribution-id/credential", nil, map[string]string{
"taskId": "task-id",
"contributionId": "contribution-id",
})
h.ResolveTaskRemoteMCPCredential(recorder, request)
if recorder.Code != http.StatusForbidden {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestResolveTaskRemoteMCPCredentialRejectsDifferentDaemonInWorkspace(t *testing.T) {
if testHandler == nil || testPool == nil {
t.Skip("database not available")
}
fixture := createCommentDeliveryFixture(t, "Remote MCP daemon isolation")
request := newDaemonTokenRequest(http.MethodGet,
"/api/daemon/tasks/"+fixture.taskID+"/remote-mcp/contribution-id/credential",
nil, testWorkspaceID, "different-daemon")
routeCtx := chi.NewRouteContext()
routeCtx.URLParams.Add("taskId", fixture.taskID)
routeCtx.URLParams.Add("contributionId", "contribution-id")
request = request.WithContext(context.WithValue(request.Context(), chi.RouteCtxKey, routeCtx))
recorder := httptest.NewRecorder()
testHandler.ResolveTaskRemoteMCPCredential(recorder, request)
if recorder.Code != http.StatusNotFound {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}