a73x

internal/smoke/mcp_contract_test.go

Ref:   Size: 2.1 KiB   History

package smoke

import (
	"context"
	"testing"

	"github.com/modelcontextprotocol/go-sdk/mcp"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/a73x/eitri/internal/mcpserver"
)

// stubDelegator satisfies mcpserver.Delegator so NewServer builds the FULL
// remote toolset: the two delegate tools are registered only when a Delegator
// is present, and a bearer-PAT remote caller always gets one.
type stubDelegator struct{}

func (stubDelegator) Begin(context.Context) (mcpserver.BeginResult, error) {
	return mcpserver.BeginResult{}, nil
}

func (stubDelegator) Complete(context.Context, string) (mcpserver.DelegationResult, error) {
	return mcpserver.DelegationResult{}, nil
}

// TestRemoteToolsMatchTheServerThatPublishesThem ties the smoke's idea of the
// remote toolset to the server that actually serves it. remoteToolCount (a
// production const the leg checks against) and the fake's scripted tool list
// agreed only with each other; a tool added to mcpserver.NewServer would leave
// both silently stale, and the gate would keep checking a number that no longer
// describes the endpoint. Listing NewServer's real tools makes that drift fail
// here instead.
func TestRemoteToolsMatchTheServerThatPublishesThem(t *testing.T) {
	srv := mcpserver.NewServer(&mcpserver.Tools{}, mcpserver.Options{Delegator: stubDelegator{}})

	ctx := t.Context()
	serverTr, clientTr := mcp.NewInMemoryTransports()
	ss, err := srv.Connect(ctx, serverTr, nil)
	require.NoError(t, err)
	defer ss.Close()
	cs, err := mcp.NewClient(&mcp.Implementation{Name: "smoke-contract", Version: "0"}, nil).Connect(ctx, clientTr, nil)
	require.NoError(t, err)
	defer cs.Close()

	res, err := cs.ListTools(ctx, nil)
	require.NoError(t, err)
	names := make([]string, 0, len(res.Tools))
	for _, tool := range res.Tools {
		names = append(names, tool.Name)
	}

	assert.Len(t, names, remoteToolCount,
		"remoteToolCount is stale: mcpserver.NewServer now publishes a different number of tools than the leg checks for")
	assert.ElementsMatch(t, newFakeMCP(t).tools, names,
		"the fake MCP's scripted tool list has drifted from what mcpserver.NewServer publishes")
}