// Package mcp is Maven's Model Context Protocol CLIENT. She is a host: she
// connects OUT to MCP servers, discovers the tools and resources they offer,
// and hands them to the parts of her that already exist for this — the tool
// allowlist in the store, the confirm turn for anything that mutates, the
// stage-3 gate that makes an uncertain act ask instead of run.
//
// She is not an MCP server. Nothing here exposes her own capabilities to an
// outside caller; docs/plans/06-mcp-support.md asks for the host direction only.
//
// Boundaries, in code rather than in prose:
//
//   - OFF unless configured. No mcp_servers block ⇒ no manager, no goroutine,
//     no socket.
//   - A remote server is reached through internal/webfetch, so the SSRF guard,
//     the size cap, the redirect cap and the per-host rate limit all apply to
//     an MCP endpoint exactly as they do to a news feed. Reaching a loopback
//     or LAN server means explicitly setting allow_private on THAT server —
//     a different trust level, spelled out per server rather than globally.
//   - Only the tool name and the arguments the router produced are sent. This
//     package never sees his notes, facts, history or the persona block, and
//     has no API through which a caller could pass them.
//   - Discovery proposes, it does not enable. A discovered tool lands as a
//     'proposed' row; a human enables it on the authed surface.
package mcp

import (
	"context"
	"encoding/json"
	"fmt"
)

// ProtocolVersion — the spec revision we ask for in the initialize handshake.
// A server that answers with a different one is accepted (the spec says the
// client may proceed if it can support what came back); we only refuse when it
// answers with no version at all, which means it is not an MCP server.
const ProtocolVersion = "2025-06-18"

// rpcRequest / rpcResponse — JSON-RPC 2.0. Deliberately hand-rolled: the wire
// format is four fields, and the repo vendors its dependencies, so pulling a
// library in for this would cost more than it saves.
type rpcRequest struct {
	JSONRPC string `json:"jsonrpc"`
	ID      int64  `json:"id,omitempty"`
	Method  string `json:"method"`
	Params  any    `json:"params,omitempty"`
}

type rpcResponse struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      *int64          `json:"id"`
	Result  json.RawMessage `json:"result,omitempty"`
	Error   *rpcError       `json:"error,omitempty"`
}

type rpcError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func (e *rpcError) Error() string { return fmt.Sprintf("mcp: rpc error %d: %s", e.Code, e.Message) }

// transport carries one JSON-RPC conversation. Implementations: stdioTransport
// (a subprocess on this box) and httpTransport (streamable HTTP, guarded by
// webfetch). Both must be safe for concurrent use by the Client.
type transport interface {
	// Call sends a request and returns the matching response.
	Call(ctx context.Context, req *rpcRequest) (*rpcResponse, error)
	// Notify sends a notification (no id, no reply expected).
	Notify(ctx context.Context, method string, params any) error
	// Close releases the transport (kills the subprocess, drops the session).
	Close() error
}
