Documentation
¶
Overview ¶
Package mcp is the MCP resource server: the read-only tool table served on /mcp, the adapters that make its authentication and authorization identical to the ConnectRPC path, and the guidance it publishes to clients.
The design is in .agents/docs/mcp-server.md and the way a client uses it is in docs/mcp.md. `sdk_contract_test.go` pins the SDK behaviours this package depends on, `tool_guard_test.go` pins the invariant that a tool cannot require less than the RPC it wraps, and `tool_test.go` pins what each tool answers.
Index ¶
- func NewTokenVerifier(endpoints EndpointsFunc, tokens *auth.TokenAuthenticator) sdkauth.TokenVerifier
- func PeerAddress(request *mcpsdk.CallToolRequest) string
- func UserFromRequest(req *mcpsdk.CallToolRequest) (*store.UserMessage, bool)
- func UserFromTokenInfo(info *sdkauth.TokenInfo) (*store.UserMessage, bool)
- type CallLimiter
- type Config
- type DatabaseReader
- type EndpointsFunc
- type InstanceReader
- type LineageReader
- type PermissionChecker
- type PrincipalReader
- type Server
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewTokenVerifier ¶
func NewTokenVerifier(endpoints EndpointsFunc, tokens *auth.TokenAuthenticator) sdkauth.TokenVerifier
NewTokenVerifier returns the bearer-token verifier for the MCP endpoint.
The token is resolved by the same authenticator the ConnectRPC API uses, but against the MCP resource audience: a web or CLI token is refused here, and so is a token minted for another deployment. Because that shared chain also checks revocation, deactivation and the password-change cutoff, a user who has been deactivated stops being accepted here at the same moment as everywhere else — which is exactly what a second, ad-hoc verification path would have missed.
func PeerAddress ¶
func PeerAddress(request *mcpsdk.CallToolRequest) string
PeerAddress returns the client address the verifier saw for a tool call, which is what the audit row resolves against the trusted-proxy rules.
func UserFromRequest ¶
func UserFromRequest(req *mcpsdk.CallToolRequest) (*store.UserMessage, bool)
UserFromRequest returns the principal the verifier resolved for a tool call. A tool that finds nothing here was reached without the bearer middleware, and must refuse rather than run without an authorization check.
func UserFromTokenInfo ¶
func UserFromTokenInfo(info *sdkauth.TokenInfo) (*store.UserMessage, bool)
UserFromTokenInfo returns the principal the verifier resolved, if any.
Types ¶
type CallLimiter ¶
CallLimiter decides whether one principal may make another tool call. It is an interface so the MCP server does not depend on the process-local state package; *state.WindowLimiter is what a deployment wires.
type Config ¶
type Config struct {
Instances InstanceReader
Databases DatabaseReader
Lineage LineageReader
Principals PrincipalReader
Checker PermissionChecker
// CallLimiter bounds how often one principal may call a tool. Wiring it is what
// keeps a token holder from turning the endpoint into a ledger and query pump;
// a test that does not exercise the budget leaves it nil.
CallLimiter CallLimiter
// SourceCallLimiter bounds how often one source address may reach the endpoint
// at all, counted before the bearer check. CallLimiter cannot see a request
// that carries no or an invalid token — it is refused during identity
// resolution, before the tool path — so without this an unauthenticated
// caller can drive token verification, and a revocation lookup for every
// token it invents, without limit. It is deliberately far looser than
// CallLimiter so it bounds probing without pacing use: agents behind one
// address share it, and a fleet of them is normal.
SourceCallLimiter CallLimiter
Stores *store.Store
// TrustedProxies decides whether a forwarded address may be believed in the
// audit row.
TrustedProxies []string
// Endpoints resolves the deployment's MCP resource identifier per request and
// says whether the surface is enabled.
Endpoints oauth.EndpointsFunc
// Now lets a test pin the clock the audit rows measure against.
Now func() time.Time
}
Config is everything the MCP server needs. Services are required; Stores and TrustedProxies are only used to write the per-call audit row, and Endpoints decides whether the surface is served at all.
type DatabaseReader ¶
type DatabaseReader interface {
ListDatabases(context.Context, *connect.Request[v1pb.ListDatabasesRequest]) (*connect.Response[v1pb.ListDatabasesResponse], error)
ListMetadata(context.Context, *connect.Request[v1pb.ListMetadataRequest]) (*connect.Response[v1pb.MetadataResponse], error)
GetMetadata(context.Context, *connect.Request[v1pb.GetMetadataRequest]) (*connect.Response[v1pb.GetMetadataResponse], error)
SearchMetadata(context.Context, *connect.Request[v1pb.SearchMetadataRequest]) (*connect.Response[v1pb.SearchMetadataResponse], error)
GetSchemaString(context.Context, *connect.Request[v1pb.GetSchemaStringRequest]) (*connect.Response[v1pb.MetadataSchemaString], error)
}
DatabaseReader is the slice of DatabaseService the tools use.
type EndpointsFunc ¶
type EndpointsFunc = oauth.EndpointsFunc
EndpointsFunc is the OAuth package's resolver type, aliased here because the verifier is wired from the same place the protocol endpoints are.
type InstanceReader ¶
type InstanceReader interface {
ListInstances(context.Context, *connect.Request[v1pb.ListInstancesRequest]) (*connect.Response[v1pb.ListInstancesResponse], error)
}
InstanceReader is the slice of InstanceService the tools use.
type LineageReader ¶
type LineageReader interface {
AnalyzeSQL(context.Context, *connect.Request[v1pb.AnalyzeSQLRequest]) (*connect.Response[v1pb.AnalyzeSQLResponse], error)
GetLineageGraph(context.Context, *connect.Request[v1pb.GetLineageGraphRequest]) (*connect.Response[v1pb.GetLineageGraphResponse], error)
}
LineageReader is the slice of LineageService the tools use.
type PermissionChecker ¶
type PermissionChecker interface {
CheckPermission(ctx context.Context, perm permission.Permission, user *store.UserMessage) (bool, error)
}
PermissionChecker resolves whether a caller holds a permission. *iam.Manager is the production implementation; the interface lets a test decide the answer.
type PrincipalReader ¶
type PrincipalReader interface {
GetCurrentUser(context.Context, *connect.Request[emptypb.Empty]) (*connect.Response[v1pb.User], error)
}
PrincipalReader is the slice of UserService the tools use.