Documentation
¶
Overview ¶
Package resolvemcp exposes registered database models as Model Context Protocol (MCP) tools and resources over HTTP/SSE transport.
It mirrors the resolvespec package patterns:
- Same model registration API
- Same filter, sort, cursor pagination, preload options
- Same lifecycle hook system
Usage:
handler := resolvemcp.NewHandlerWithGORM(db, resolvemcp.Config{BaseURL: "http://localhost:8080"})
handler.RegisterModel("public", "users", &User{})
r := mux.NewRouter()
resolvemcp.SetupMuxRoutes(r, handler, securityList) // requires an authenticated caller
Index ¶
- Constants
- func GetEntity(ctx context.Context) string
- func GetModel(ctx context.Context) interface{}
- func GetModelPtr(ctx context.Context) interface{}
- func GetSchema(ctx context.Context) string
- func GetTableName(ctx context.Context) string
- func Guard(securityList *security.SecurityList) func(http.Handler) http.Handler
- func NewClientError(code, message string) error
- func NewSSEServer(handler *Handler, securityList *security.SecurityList) http.Handler
- func NewStreamableHTTPHandler(handler *Handler, securityList *security.SecurityList) http.Handler
- func OAuth2CallbackHandler(auth *security.DatabaseAuthenticator, providerName, afterLoginRedirect string, ...) http.HandlerFunc
- func OAuth2LoginHandler(auth *security.DatabaseAuthenticator, providerName string) http.HandlerFunc
- func RegisterSecurityHooks(handler *Handler, securityList *security.SecurityList)
- func SetupBunRouterRoutes(router *bunrouter.Router, handler *Handler, ...)
- func SetupBunRouterRoutesUnauthenticated(router *bunrouter.Router, handler *Handler)
- func SetupBunRouterStreamableHTTPRoutes(router *bunrouter.Router, handler *Handler, ...)
- func SetupBunRouterStreamableHTTPRoutesUnauthenticated(router *bunrouter.Router, handler *Handler)
- func SetupMuxOAuth2Routes(muxRouter *mux.Router, auth *security.DatabaseAuthenticator, ...)
- func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler, securityList *security.SecurityList)
- func SetupMuxRoutesUnauthenticated(muxRouter *mux.Router, handler *Handler)
- func SetupMuxStreamableHTTPRoutes(muxRouter *mux.Router, handler *Handler, securityList *security.SecurityList)
- func SetupMuxStreamableHTTPRoutesUnauthenticated(muxRouter *mux.Router, handler *Handler)
- func WithEntity(ctx context.Context, entity string) context.Context
- func WithModel(ctx context.Context, model interface{}) context.Context
- func WithModelPtr(ctx context.Context, modelPtr interface{}) context.Context
- func WithSchema(ctx context.Context, schema string) context.Context
- func WithTableName(ctx context.Context, tableName string) context.Context
- type ClientError
- type Config
- type Function
- type FunctionFunc
- type FunctionParam
- type Handler
- func (h *Handler) AuthedSSEServer(securityList *security.SecurityList) http.Handler
- func (h *Handler) AuthedStreamableHTTPServer(securityList *security.SecurityList) http.Handler
- func (h *Handler) EnableOAuthServer(cfg security.OAuthServerConfig, auth *security.DatabaseAuthenticator)
- func (h *Handler) GetDatabase() common.Database
- func (h *Handler) HTTPHandler(securityList *security.SecurityList) http.Handler
- func (h *Handler) Hooks() *HookRegistry
- func (h *Handler) MCPServer() *server.MCPServer
- func (h *Handler) RegisterFunction(f Function) error
- func (h *Handler) RegisterModel(schema, entity string, model interface{}) error
- func (h *Handler) RegisterModelWithRules(schema, entity string, model interface{}, rules modelregistry.ModelRules) error
- func (h *Handler) RegisterOAuth2(auth *security.DatabaseAuthenticator, cfg OAuth2RouteConfig)
- func (h *Handler) RegisterOAuth2Provider(auth *security.DatabaseAuthenticator, providerName string)
- func (h *Handler) SSEServer() http.Handler
- func (h *Handler) SetModelRules(schema, entity string, rules modelregistry.ModelRules) error
- func (h *Handler) StreamableHTTPMux(securityList *security.SecurityList) http.Handler
- func (h *Handler) StreamableHTTPServer() http.Handler
- type HookContext
- type HookFunc
- type HookRegistry
- func (r *HookRegistry) Clear(hookType HookType)
- func (r *HookRegistry) ClearAll()
- func (r *HookRegistry) Execute(hookType HookType, ctx *HookContext) error
- func (r *HookRegistry) HasHooks(hookType HookType) bool
- func (r *HookRegistry) Register(hookType HookType, hook HookFunc)
- func (r *HookRegistry) RegisterMultiple(hookTypes []HookType, hook HookFunc)
- type HookType
- type OAuth2RouteConfig
Constants ¶
const ( CodeInvalidArgument = "invalid_argument" CodeNotFound = "not_found" CodeForbidden = "forbidden" CodeLimitExceeded = "limit_exceeded" CodeInternal = "internal" )
Stable error codes returned to MCP clients.
const ( ParamString = "string" ParamInteger = "integer" ParamNumber = "number" ParamBoolean = "boolean" ParamObject = "object" ParamArray = "array" )
Parameter types a function may declare.
Variables ¶
This section is empty.
Functions ¶
func GetModelPtr ¶
func GetTableName ¶
func Guard ¶ added in v1.2.12
Guard returns middleware that requires an authenticated caller on every request.
The security list's provider decides which credentials are accepted: build it from a security.ChainAuthenticator over an OAuth bearer token, a session token (header or cookie) and an API key authenticator. The authenticated security.UserContext is placed in the request context, which the MCP transports pass on to every tool call, so rules, row security and OnTxBegin apply to that caller.
Unlike security.NewAuthMiddleware this guard has no guest or optional mode: it ignores security.SkipAuth / security.OptionalAuth markers on the request context, and fails closed (500) when no provider is configured.
func NewClientError ¶ added in v1.2.12
NewClientError returns an error that reaches the client as {code, message}.
func NewSSEServer ¶
func NewSSEServer(handler *Handler, securityList *security.SecurityList) http.Handler
NewSSEServer returns an http.Handler that serves MCP over SSE behind Guard(securityList). If Config.BasePath is set it is used directly; otherwise the base path is detected from each incoming request (by stripping the "/sse" or "/message" suffix).
h := resolvemcp.NewSSEServer(handler, securityList)
http.Handle("/api/mcp/", h)
func NewStreamableHTTPHandler ¶ added in v1.0.75
func NewStreamableHTTPHandler(handler *Handler, securityList *security.SecurityList) http.Handler
NewStreamableHTTPHandler returns an http.Handler that serves MCP over the streamable HTTP transport behind Guard(securityList). Mount it at the desired path; that path becomes the MCP endpoint.
h := resolvemcp.NewStreamableHTTPHandler(handler, securityList)
http.Handle("/mcp", h)
engine.Any("/mcp", gin.WrapH(h))
func OAuth2CallbackHandler ¶ added in v1.0.78
func OAuth2CallbackHandler(auth *security.DatabaseAuthenticator, providerName, afterLoginRedirect string, cookieOpts ...security.SessionCookieOptions) http.HandlerFunc
OAuth2CallbackHandler returns an http.HandlerFunc that handles the OAuth2 provider callback: exchanges the authorization code for a session token, writes the session cookie, then either redirects to afterLoginRedirect or writes the LoginResponse as JSON.
Register it on any router:
mux.Handle("/auth/google/callback", resolvemcp.OAuth2CallbackHandler(auth, "google", "/dashboard"))
func OAuth2LoginHandler ¶ added in v1.0.78
func OAuth2LoginHandler(auth *security.DatabaseAuthenticator, providerName string) http.HandlerFunc
OAuth2LoginHandler returns an http.HandlerFunc that redirects the browser to the OAuth2 provider's authorization URL.
Register it on any router:
mux.Handle("/auth/google/login", resolvemcp.OAuth2LoginHandler(auth, "google"))
func RegisterSecurityHooks ¶ added in v1.0.74
func RegisterSecurityHooks(handler *Handler, securityList *security.SecurityList)
RegisterSecurityHooks wires the security package's access-control layer into the resolvemcp handler. Call it once after creating the handler, before registering models.
The following controls are applied:
- Per-entity operation rules (CanRead, CanCreate, CanUpdate, CanDelete, CanPublic*) stored via RegisterModelWithRules / SetModelRules.
- Row-level security: WHERE clause injected per user from the SecurityList provider.
- Column-level security: sensitive columns masked/hidden in read results.
- Audit logging after each read.
func SetupBunRouterRoutes ¶ added in v1.0.69
func SetupBunRouterRoutes(router *bunrouter.Router, handler *Handler, securityList *security.SecurityList)
SetupBunRouterRoutes mounts the MCP HTTP/SSE endpoints on a bunrouter router using the base path from Config.BasePath, behind Guard(securityList).
Routes registered:
- GET {basePath}/sse — SSE connection endpoint
- POST {basePath}/message — JSON-RPC message endpoint
func SetupBunRouterRoutesUnauthenticated ¶ added in v1.2.12
SetupBunRouterRoutesUnauthenticated is SetupBunRouterRoutes without the guard. A warning is logged.
func SetupBunRouterStreamableHTTPRoutes ¶ added in v1.0.75
func SetupBunRouterStreamableHTTPRoutes(router *bunrouter.Router, handler *Handler, securityList *security.SecurityList)
SetupBunRouterStreamableHTTPRoutes mounts the MCP streamable HTTP endpoint on a bunrouter router, behind Guard(securityList). The transport uses a single endpoint (Config.BasePath).
func SetupBunRouterStreamableHTTPRoutesUnauthenticated ¶ added in v1.2.12
SetupBunRouterStreamableHTTPRoutesUnauthenticated is SetupBunRouterStreamableHTTPRoutes without the guard. A warning is logged.
func SetupMuxOAuth2Routes ¶ added in v1.0.78
func SetupMuxOAuth2Routes(muxRouter *mux.Router, auth *security.DatabaseAuthenticator, cfg OAuth2RouteConfig)
SetupMuxOAuth2Routes registers the OAuth2 login and callback routes on a Gorilla Mux router.
Example:
resolvemcp.SetupMuxOAuth2Routes(r, auth, resolvemcp.OAuth2RouteConfig{
ProviderName: "google", LoginPath: "/auth/google/login",
CallbackPath: "/auth/google/callback", AfterLoginRedirect: "/",
})
func SetupMuxRoutes ¶
func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler, securityList *security.SecurityList)
SetupMuxRoutes mounts the MCP HTTP/SSE endpoints on the given Gorilla Mux router using the base path from Config.BasePath, behind Guard(securityList).
Routes registered:
- GET {basePath}/sse — SSE connection endpoint (client subscribes here)
- POST {basePath}/message — JSON-RPC message endpoint (client sends requests here)
Nothing is mounted (and an error is logged) when securityList has no provider.
func SetupMuxRoutesUnauthenticated ¶ added in v1.2.12
SetupMuxRoutesUnauthenticated is SetupMuxRoutes without the guard. Every caller reaches every registered model, so use it only behind another trusted layer. A warning is logged.
func SetupMuxStreamableHTTPRoutes ¶ added in v1.0.75
func SetupMuxStreamableHTTPRoutes(muxRouter *mux.Router, handler *Handler, securityList *security.SecurityList)
SetupMuxStreamableHTTPRoutes mounts the MCP streamable HTTP endpoint on the given Gorilla Mux router, behind Guard(securityList). The streamable HTTP transport uses a single endpoint (Config.BasePath) for all communication: POST for client→server messages, GET for server→client streaming.
Nothing is mounted (and an error is logged) when securityList has no provider.
func SetupMuxStreamableHTTPRoutesUnauthenticated ¶ added in v1.2.12
SetupMuxStreamableHTTPRoutesUnauthenticated is SetupMuxStreamableHTTPRoutes without the guard. A warning is logged.
Types ¶
type ClientError ¶ added in v1.2.12
ClientError is an error whose code and message are safe to show to an MCP client. Hooks may return one (NewClientError) to give the client a specific reason; every other error is reported as an opaque internal error with a reference that matches the server log.
func (*ClientError) Error ¶ added in v1.2.12
func (e *ClientError) Error() string
type Config ¶ added in v1.0.70
type Config struct {
// BaseURL is the public-facing base URL of the server (e.g. "http://localhost:8080").
// It is sent to MCP clients during the SSE handshake so they know where to POST messages.
BaseURL string
// BasePath is the URL path prefix where the MCP endpoints are mounted (e.g. "/mcp").
// If empty, the path is detected from each incoming request automatically.
BasePath string
// DefaultLimit is the page size when a read gives no limit (50).
DefaultLimit int
// MaxLimit caps a read's limit; larger values are clamped (1000).
MaxLimit int
// MaxOffset rejects a read whose offset is larger (100000).
MaxOffset int
// MaxBatch caps the items in one batch create (100).
MaxBatch int
// MaxPreloadDepth caps the depth of a preload path such as "a.b.c" (2).
MaxPreloadDepth int
// MaxWriteRows caps the rows a filter-based update or delete may touch (100).
MaxWriteRows int
// QueryTimeout bounds one tool call, hooks and queries included (30s).
QueryTimeout time.Duration
// ConfirmTTL is how long a confirmation token for a filter write stays valid (5m).
ConfirmTTL time.Duration
// AllowedHosts restricts the Host header accepted by the SSE transport when BaseURL is
// empty (the message endpoint URL sent to clients is built from it). Empty accepts any
// host, with at most 32 distinct base URLs cached; prefer setting BaseURL.
AllowedHosts []string
// EnableAnnotations registers the resolvespec_annotate tool. Off by default: annotations
// are free text that agents read back, so enabling the tool opens a write channel into
// agent-visible text. When on, every call runs the BeforeHandle hooks (operation
// "annotate_set" / "annotate_get") and the writes run in a transaction with OnTxBegin.
EnableAnnotations bool
}
Config holds configuration for the resolvemcp handler.
type Function ¶ added in v1.2.12
type Function struct {
Name string
Description string
Params []FunctionParam
// Handler is the Go callback.
Handler FunctionFunc
// Procedure is the SQL function to call, optionally schema-qualified. Declared params are
// passed positionally in declaration order; an omitted optional param is NULL. Rows
// returned are the result.
Procedure string
// Authorize, when set, decides per caller whether the function is listed and callable.
// Return nil to allow.
Authorize func(ctx context.Context) error
}
Function is a callable registered with Handler.RegisterFunction. Exactly one of Handler (a Go callback) and Procedure (a SQL function called by name) is set.
type FunctionFunc ¶ added in v1.2.12
FunctionFunc is a Go function callable through call_function. tx is the transaction the call runs in (OnTxBegin already fired on it); args are validated against the declared params.
type FunctionParam ¶ added in v1.2.12
type FunctionParam struct {
Name string
Type string // one of the Param* constants
Description string
Required bool
}
FunctionParam declares one argument of a registered function.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler exposes registered database models as MCP tools and resources.
func NewHandler ¶
NewHandler creates a Handler with the given database, model registry, and config.
func NewHandlerWithBun ¶
NewHandlerWithBun creates a Handler backed by a Bun database connection.
func NewHandlerWithDB ¶
NewHandlerWithDB creates a Handler using an existing common.Database and a new registry.
func NewHandlerWithGORM ¶
NewHandlerWithGORM creates a Handler backed by a GORM database connection.
func (*Handler) AuthedSSEServer ¶ added in v1.0.78
func (h *Handler) AuthedSSEServer(securityList *security.SecurityList) http.Handler
AuthedSSEServer wraps SSEServer with required authentication middleware from pkg/security. The middleware reads the session cookie / Authorization header and populates the user context into the request context, making it available to BeforeHandle security hooks. Unauthenticated requests receive 401 before reaching any MCP tool.
func (*Handler) AuthedStreamableHTTPServer ¶ added in v1.0.78
func (h *Handler) AuthedStreamableHTTPServer(securityList *security.SecurityList) http.Handler
AuthedStreamableHTTPServer wraps StreamableHTTPServer with required authentication middleware.
func (*Handler) EnableOAuthServer ¶ added in v1.0.78
func (h *Handler) EnableOAuthServer(cfg security.OAuthServerConfig, auth *security.DatabaseAuthenticator)
EnableOAuthServer activates the MCP-standard OAuth2 authorization server on this Handler.
Pass a DatabaseAuthenticator to enable direct username/password login — the server acts as its own identity provider and renders a login form at /oauth/authorize. Pass nil to use only external providers registered via RegisterOAuth2Provider.
After calling this, HTTPHandler and StreamableHTTPMux serve the full set of RFC-compliant endpoints required by MCP clients alongside the MCP transport:
GET /.well-known/oauth-authorization-server RFC 8414 — auto-discovery POST /oauth/register RFC 7591 — dynamic client registration GET /oauth/authorize OAuth 2.1 + PKCE — start login POST /oauth/authorize Login form submission (password flow) POST /oauth/token Bearer token exchange + refresh GET /oauth/provider/callback External provider redirect target
func (*Handler) GetDatabase ¶
GetDatabase returns the underlying database.
func (*Handler) HTTPHandler ¶ added in v1.0.78
func (h *Handler) HTTPHandler(securityList *security.SecurityList) http.Handler
HTTPHandler returns a single http.Handler that serves:
- MCP OAuth2 authorization server endpoints (when EnableOAuthServer has been called)
- OAuth2 login + callback routes for every registered provider (legacy cookie flow)
- The MCP SSE transport wrapped with required authentication middleware
Example:
auth := security.NewGoogleAuthenticator(...)
handler.RegisterOAuth2(auth, cfg)
handler.EnableOAuthServer(security.OAuthServerConfig{Issuer: "https://api.example.com"})
security.RegisterSecurityHooks(handler, securityList)
http.ListenAndServe(":8080", handler.HTTPHandler(securityList))
func (*Handler) RegisterFunction ¶ added in v1.2.12
RegisterFunction makes f callable through the call_function meta tool. Only registered functions are callable; there is no way to reach an arbitrary SQL function.
func (*Handler) RegisterModel ¶
RegisterModel registers a model. It becomes visible to the fixed meta tools (list_tables, select_table, ...); no per-model tools are created.
func (*Handler) RegisterModelWithRules ¶ added in v1.0.74
func (h *Handler) RegisterModelWithRules(schema, entity string, model interface{}, rules modelregistry.ModelRules) error
RegisterModelWithRules registers a model and sets per-entity operation rules (CanRead, CanCreate, CanUpdate, CanDelete, CanPublic*, SecurityDisabled). Requires RegisterSecurityHooks to have been called for the rules to be enforced.
func (*Handler) RegisterOAuth2 ¶ added in v1.0.78
func (h *Handler) RegisterOAuth2(auth *security.DatabaseAuthenticator, cfg OAuth2RouteConfig)
RegisterOAuth2 attaches an OAuth2 provider to the Handler. The login and callback HTTP routes are served by HTTPHandler / StreamableHTTPMux. Call this once per provider before serving requests.
Example:
auth := security.NewGoogleAuthenticator(clientID, secret, redirectURL, db)
handler.RegisterOAuth2(auth, resolvemcp.OAuth2RouteConfig{
ProviderName: "google",
LoginPath: "/auth/google/login",
CallbackPath: "/auth/google/callback",
AfterLoginRedirect: "/",
})
func (*Handler) RegisterOAuth2Provider ¶ added in v1.0.78
func (h *Handler) RegisterOAuth2Provider(auth *security.DatabaseAuthenticator, providerName string)
RegisterOAuth2Provider adds an external OAuth2 provider to the MCP OAuth2 authorization server. EnableOAuthServer must be called before this. The auth must have been configured with WithOAuth2(providerName, ...) for the given provider name.
func (*Handler) SSEServer ¶ added in v1.0.69
SSEServer returns an http.Handler that serves MCP over SSE. Config.BasePath must be set. Config.BaseURL is used when set; if empty it is detected automatically from each incoming request.
func (*Handler) SetModelRules ¶ added in v1.0.74
func (h *Handler) SetModelRules(schema, entity string, rules modelregistry.ModelRules) error
SetModelRules updates the operation rules for an already-registered model. Requires RegisterSecurityHooks to have been called for the rules to be enforced.
func (*Handler) StreamableHTTPMux ¶ added in v1.0.78
func (h *Handler) StreamableHTTPMux(securityList *security.SecurityList) http.Handler
StreamableHTTPMux returns a single http.Handler that serves:
- MCP OAuth2 authorization server endpoints (when EnableOAuthServer has been called)
- OAuth2 login + callback routes for every registered provider (legacy cookie flow)
- The MCP streamable HTTP transport wrapped with required authentication middleware
Example:
http.ListenAndServe(":8080", handler.StreamableHTTPMux(securityList))
func (*Handler) StreamableHTTPServer ¶ added in v1.0.75
StreamableHTTPServer returns an http.Handler that serves MCP over the streamable HTTP transport. Unlike SSE (which requires two endpoints), streamable HTTP uses a single endpoint for all client-server communication (POST for requests, GET for server-initiated messages). Mount the returned handler at the desired path; the path itself becomes the MCP endpoint.
type HookContext ¶
type HookContext struct {
Context context.Context
Handler *Handler
Schema string
Entity string
Model interface{}
Options common.RequestOptions
Operation string
ID string
Data interface{}
Result interface{}
Error error
Query common.SelectQuery
Abort bool
AbortMessage string
AbortCode int
Tx common.Database
}
HookContext contains all the data available to a hook
func (*HookContext) SetTx ¶ added in v1.2.7
func (c *HookContext) SetTx(tx common.Database)
SetTx points the context at the transaction in use (common.TxContext).
type HookRegistry ¶
type HookRegistry struct {
// contains filtered or unexported fields
}
HookRegistry manages all registered hooks
func NewHookRegistry ¶
func NewHookRegistry() *HookRegistry
func (*HookRegistry) Clear ¶
func (r *HookRegistry) Clear(hookType HookType)
func (*HookRegistry) ClearAll ¶
func (r *HookRegistry) ClearAll()
func (*HookRegistry) Execute ¶
func (r *HookRegistry) Execute(hookType HookType, ctx *HookContext) error
func (*HookRegistry) HasHooks ¶
func (r *HookRegistry) HasHooks(hookType HookType) bool
func (*HookRegistry) Register ¶
func (r *HookRegistry) Register(hookType HookType, hook HookFunc)
func (*HookRegistry) RegisterMultiple ¶
func (r *HookRegistry) RegisterMultiple(hookTypes []HookType, hook HookFunc)
type HookType ¶
type HookType string
HookType defines the type of hook to execute
const ( // BeforeHandle fires after model resolution, before operation dispatch. BeforeHandle HookType = "before_handle" BeforeRead HookType = "before_read" AfterRead HookType = "after_read" BeforeCreate HookType = "before_create" AfterCreate HookType = "after_create" // BeforeScan fires on update and delete, with hookCtx.Query set to the select that loads the // target row. Hooks that narrow the query (row security) run here; a row the query does // not return is reported as not found and never written. BeforeScan HookType = "before_scan" BeforeUpdate HookType = "before_update" AfterUpdate HookType = "after_update" BeforeDelete HookType = "before_delete" AfterDelete HookType = "after_delete" // BeforeCall and AfterCall fire inside the transaction of a call_function call. // hookCtx.Entity is the function name, Data the validated arguments (BeforeCall may // replace them) and Result the function's result (AfterCall). BeforeCall HookType = "before_call" AfterCall HookType = "after_call" // OnTxBegin fires once, first, inside every transaction the handler opens // (including the second short transaction for post-commit work). hookCtx.Tx is // the transaction; use it to stamp transaction-local state such as RLS // settings. An error or abort rolls the transaction back. OnTxBegin HookType = common.TxHookName )
type OAuth2RouteConfig ¶ added in v1.0.78
type OAuth2RouteConfig struct {
// ProviderName is the OAuth2 provider name as registered with WithOAuth2()
// (e.g. "google", "github", "microsoft").
ProviderName string
// LoginPath is the HTTP path that redirects the browser to the OAuth2 provider
// (e.g. "/auth/google/login").
LoginPath string
// CallbackPath is the HTTP path that the OAuth2 provider redirects back to
// (e.g. "/auth/google/callback"). Must match the RedirectURL in OAuth2Config.
CallbackPath string
// AfterLoginRedirect is the URL to redirect the browser to after a successful
// login. When empty the LoginResponse JSON is written directly to the response.
AfterLoginRedirect string
// CookieOptions customises the session cookie written on successful login.
// Defaults to HttpOnly, Secure, SameSite=Lax when nil.
CookieOptions *security.SessionCookieOptions
}
OAuth2RouteConfig configures the OAuth2 HTTP endpoints for a single provider.