Documentation
¶
Overview ¶
Package apigateway provides an HTTP API gateway toolkit that proxies authenticated REST API calls through the platform's auth, persona, and audit pipeline. Sibling to pkg/toolkits/gateway, which proxies upstream MCP servers; this toolkit proxies arbitrary HTTP/JSON APIs.
The toolkit exposes a small fixed set of MCP tools regardless of how many connections are registered or how many endpoints each upstream API has. v1 ships api_invoke_endpoint; api_list_endpoints and api_get_endpoint_schema follow once OpenAPI ingestion lands (see the RFC at issue #364).
Index ¶
- Constants
- Variables
- func ClassifyInvokeOutcome(out InvokeOutput) string
- func ComputeOperationEmbeddings(ctx context.Context, embedder embedding.Provider, content, specName string, ...) ([]catalog.OperationEmbedding, error)
- type Authenticator
- type Config
- type EndpointSchemaOutput
- type ExportAsset
- type ExportAssetRef
- type ExportAssetStore
- type ExportConfig
- type ExportDeps
- type ExportProvenance
- type ExportProvenanceCall
- type ExportS3Client
- type ExportShareCreator
- type ExportUserContext
- type ExportVersion
- type ExportVersionStore
- type GetEndpointSchemaInput
- type InvokeInput
- type InvokeOutput
- type ListEndpointsInput
- type ListEndpointsOutput
- type MultiConfig
- type OAuth2Config
- type OAuthKindHandler
- type OperationSummary
- type PaginationInfo
- type ParameterDetail
- type RankingMode
- type RequestBodyDetail
- type ResponseDetail
- type RoutePolicy
- type Toolkit
- func (t *Toolkit) AddConnection(name string, config map[string]any) error
- func (t *Toolkit) CatalogStore() catalog.Store
- func (t *Toolkit) Close() error
- func (t *Toolkit) ConnOAuthStore() connoauth.Store
- func (t *Toolkit) Connection() string
- func (t *Toolkit) EmbeddingProvider() embedding.Provider
- func (t *Toolkit) HasConnection(name string) bool
- func (*Toolkit) Kind() string
- func (t *Toolkit) ListConnections() []toolkit.ConnectionDetail
- func (t *Toolkit) Name() string
- func (t *Toolkit) RegisterTools(s *mcp.Server)
- func (t *Toolkit) ReloadConnection(name string) error
- func (t *Toolkit) ReloadConnectionsByCatalog(catalogID string)
- func (t *Toolkit) RemoveConnection(name string) error
- func (t *Toolkit) RoutePolicy() RoutePolicy
- func (t *Toolkit) SetAuthEvents(w *authevents.Writer)
- func (t *Toolkit) SetCatalogStore(s catalog.Store)
- func (t *Toolkit) SetConnOAuthStore(s connoauth.Store)
- func (t *Toolkit) SetEmbeddingProvider(p embedding.Provider)
- func (t *Toolkit) SetExportDeps(deps ExportDeps)
- func (t *Toolkit) SetMetrics(m *observability.Metrics)
- func (t *Toolkit) SetQueryProvider(provider query.Provider)
- func (t *Toolkit) SetRoutePolicy(p RoutePolicy)
- func (t *Toolkit) SetSemanticProvider(provider semantic.Provider)
- func (t *Toolkit) Tools() []string
Constants ¶
const ( // Kind is the connection-instance kind discriminator. Operators see // this in the admin UI's connection picker. Kind = "api" // AuthModeNone disables outbound authentication. AuthModeNone = "none" // AuthModeBearer sends "Authorization: Bearer <credential>". AuthModeBearer = "bearer" // AuthModeAPIKey sends the credential as a header (default // "X-API-Key") or as a query parameter; placement and key name are // per-connection so APIs that use non-standard schemes (e.g. an // "api_key" query parameter, or a custom "X-Api-Token" header) can // be onboarded without code changes. AuthModeAPIKey = "api_key" // AuthModeBasic sends "Authorization: Basic base64(username:password)" // per RFC 7617. Required for the long tail of older REST APIs (Jenkins, // on-prem Jira / Confluence Server / DC, internal apps) that never moved // to bearer or OAuth. RFC 7617 §2 forbids ":" in the userid; password // may be empty (some APIs accept "token:" as a bearer-token-in-username // pattern). Password is encrypted at rest via the platform's // FieldEncryptor (the "password" config key is already in the // sensitive-keys list). AuthModeBasic = "basic" // AuthModeOAuth2ClientCredentials acquires a bearer token via // the OAuth 2.1 client_credentials grant — server-to-server, // no human in the loop. The platform exchanges the configured // client_id + client_secret for a token at OAuth.TokenURL and // applies it as "Authorization: Bearer <token>" on outbound // calls. Tokens are cached + refreshed automatically by the // underlying golang.org/x/oauth2 library; no DB state is // required because every restart can re-acquire from credentials. // The authorization_code grant (which DOES require DB-persisted // refresh tokens + a browser flow) is its own follow-up issue. AuthModeOAuth2ClientCredentials = "oauth2_client_credentials" // #nosec G101 -- mode name, not a credential // AuthModeOAuth2AuthorizationCode runs the user-driven OAuth 2.1 // authorization-code grant: an admin completes a one-time browser // flow at connection setup; the resulting refresh token is // persisted (encrypted) so subsequent platform restarts and // background workloads keep working without further interaction. // Tokens are refreshed automatically before expiry. Requires the // platform's database (refresh-token state survives restarts). AuthModeOAuth2AuthorizationCode = "oauth2_authorization_code" // #nosec G101 -- mode name, not a credential // APIKeyPlacementHeader (default) sends the credential as an HTTP // header named by APIKeyHeader. APIKeyPlacementHeader = "header" // APIKeyPlacementQuery sends the credential as a URL query parameter // named by APIKeyParam. APIKeyPlacementQuery = "query" // DefaultAPIKeyHeader is the conventional API-key header name when // the connection does not specify one. DefaultAPIKeyHeader = "X-API-Key" // #nosec G101 -- header name, not a credential // TrustLevelUntrusted is the default. Advisory only in v1: the // field is parsed and validated but no platform code reads it. // Reserved for future response-shaping enforcement (see issue // #373) — operators setting this today get the same behavior as // not setting it. TrustLevelUntrusted = "untrusted" // TrustLevelTrusted is advisory only in v1; see TrustLevelUntrusted. TrustLevelTrusted = "trusted" // DefaultConnectTimeout caps the time spent establishing the // outbound connection (TCP + TLS handshake) on each invocation. DefaultConnectTimeout = 10 * time.Second // DefaultCallTimeout caps the total per-call time including // upstream processing and response read. DefaultCallTimeout = 60 * time.Second // DefaultMaxResponseBytes caps how much of the upstream response // body the toolkit will return to the model. Larger payloads are // truncated; the response envelope flags truncation so the model // can react. Operators with a need for large bodies can raise this // per-connection or, when #372 lands, use the streaming-to-S3 // variant to bypass the model entirely. DefaultMaxResponseBytes = int64(10 * 1024 * 1024) )
const ( OAuth2AuthStyleHeader = "header" OAuth2AuthStyleParams = "params" )
EndpointAuthStyle values.
const ( // ToolInvokeEndpoint is the MCP tool name for the invoke // operation. Exported so audit code and tests reference the same // literal as the registration site. ToolInvokeEndpoint = "api_invoke_endpoint" // ToolListEndpoints names the tool that returns OperationSummary // candidates from a connection's parsed OpenAPI spec. Companion // to ToolInvokeEndpoint: the model uses list to discover what's // available, then invoke to call it. ToolListEndpoints = "api_list_endpoints" )
const ToolGetEndpointSchema = "api_get_endpoint_schema"
ToolGetEndpointSchema is the MCP tool name for the per-endpoint detail lookup. Exported so audit code and tests reference the same literal as the registration site.
Variables ¶
var ErrConnectionExists = errors.New("apigateway: connection already exists")
ErrConnectionExists is returned when AddConnection is called with a name already registered in the toolkit.
var ErrConnectionNotFound = errors.New("apigateway: connection not found")
ErrConnectionNotFound is returned when an operation is requested against a connection that has not been registered.
var ErrNeedsReauth = errors.New("apigateway: oauth2 connection needs admin reconnect")
ErrNeedsReauth is the structured error api_invoke_endpoint surfaces when an authorization_code connection's stored refresh token is missing, expired beyond refresh_expires_at, or definitively rejected by the IdP (RFC 6749 §5.2 invalid_grant on the refresh_token grant). Transient failures (network, 5xx, request cancellation) DO NOT produce this error.
The error message intentionally points the operator at the platform's reauth path rather than echoing the underlying IdP response (which can include sensitive material from a partial grant exchange).
Functions ¶
func ClassifyInvokeOutcome ¶ added in v1.64.0
func ClassifyInvokeOutcome(out InvokeOutput) string
ClassifyInvokeOutcome maps an InvokeOutput to one of the bounded outcome categories defined in pkg/observability. The audit middleware reads this category off the CallToolResult's _meta to populate audit_logs.error_category and to derive success without relying on the MCP IsError flag, which is reserved for gateway-level failures (transport / timeout) per the corrected gateway semantics described in issue #432.
Mapping:
- Status 0 with a timeout-like error message → upstream_timeout
- Status 0 with any other transport error → transport_err
- 4xx upstream response → upstream_4xx
- 5xx upstream response → upstream_5xx
- 2xx / 3xx upstream response → ok
Pattern matching on the scrubbed error string is acceptable here because scrubTransportError above normalizes the upstream error shape; the substrings checked below are the stable phrases Go's net/http and net/url error types produce.
func ComputeOperationEmbeddings ¶ added in v1.61.11
func ComputeOperationEmbeddings(ctx context.Context, embedder embedding.Provider, content, specName string, existing map[string]catalog.OperationEmbedding, progress func(completed int)) ([]catalog.OperationEmbedding, error)
ComputeOperationEmbeddings parses content as an OpenAPI document, walks its operations, and returns one catalog.OperationEmbedding per operation. For an operation whose text hashes to a value already present in existing, the existing row's Embedding is reused (no provider call) on a spec refresh. Returns nil when embedder is nil (embedding-less deployment) or when the spec parses to zero operations.
The embed text is built with an empty base path so a base_path change does not invalidate every vector. Operators tweaking the per-spec prefix shouldn't trigger a full re-embed pass.
progress is invoked at chunk boundaries during the fresh-embed pass with the cumulative number of operations whose vectors are ready (reused rows are counted up front; freshly-embedded rows are added per chunk). The embed-jobs worker uses this to publish embedded_so_far to the job row so the catalog status endpoint can render incremental progress while the final atomic upsert is still pending (#430). nil progress disables the callback for non-worker call sites (admin handler invocations that never see the long-running path).
Called by the admin handler after every spec upsert / upload / refresh / clone. Failures are non-fatal at the call site: the spec write has already succeeded; an embedding compute failure just means semantic ranking falls back to lexical until the operator runs the re-embed admin endpoint.
Types ¶
type Authenticator ¶
Authenticator applies a connection's authentication scheme to an outbound HTTP request before it is sent. Implementations must be safe for concurrent use — a single Authenticator is shared across all in-flight invocations of a connection.
Implementations MUST NOT log credential material. The toolkit's audit pipeline expects no Authorization or X-API-Key value to ever appear in slog output, error messages, or audit rows; carelessly formatted error strings are the most common leak path.
func NewAuthenticator ¶
func NewAuthenticator(c Config) (Authenticator, error)
NewAuthenticator returns the Authenticator implementation for a validated Config. ParseConfig has already rejected unknown auth modes, so the default branch only fires if a future mode is added without a matching case here.
type Config ¶
type Config struct {
// BaseURL is the upstream API root (e.g. "https://api.example.com").
// Required. Trailing slash is stripped at parse time.
BaseURL string
// AuthMode is "none", "bearer", or "api_key" in v1. OAuth modes
// land with #368.
AuthMode string
// Credential is the bearer token or API key. Ignored when AuthMode
// is "none". Encrypted at rest via the platform's FieldEncryptor.
Credential string
// APIKeyPlacement is "header" (default) or "query" — only consulted
// when AuthMode is "api_key".
APIKeyPlacement string
// APIKeyHeader is the header name to set when APIKeyPlacement is
// "header". Defaults to "X-API-Key".
APIKeyHeader string
// APIKeyParam is the query parameter name when APIKeyPlacement is
// "query". No default — required when placement is "query".
APIKeyParam string
// Username is the userid for HTTP Basic auth (RFC 7617). Required
// when AuthMode is "basic". Ignored otherwise. Not a secret on its
// own (per RFC 7617 §2 the userid is sent in clear after base64
// decoding regardless), so it is not encrypted at rest.
Username string
// Password is the password for HTTP Basic auth. May be empty: some
// legacy APIs accept a bearer token in the userid slot with an empty
// password (the "token:" pattern). Encrypted at rest via the
// platform's FieldEncryptor; the "password" cfg key is already in
// the sensitive-keys list.
Password string
// ConnectionName is the audit-visible connection identifier and the
// value passed in the tool's `connection` argument. Defaults to
// the toolkit instance name when unset.
ConnectionName string
// ConnectTimeout caps the dial step on each invocation.
ConnectTimeout time.Duration
// CallTimeout caps the total per-invocation time.
CallTimeout time.Duration
// TrustLevel is "untrusted" (default) or "trusted".
TrustLevel string
// MaxResponseBytes caps how much of an upstream response body is
// returned to the model. Defaults to DefaultMaxResponseBytes.
MaxResponseBytes int64
// CatalogID names the api_catalogs row whose component specs
// describe this connection's upstream API. Empty = no spec
// surface. The catalog is global and may back many connections;
// editing it propagates to all of them via Toolkit.ReloadConnection.
CatalogID string
// OAuth2 carries the OAuth 2.1 parameters used when AuthMode
// is oauth2_client_credentials. Empty for non-OAuth modes.
OAuth2 OAuth2Config
// StaticHeaders are operator-configured headers attached to every
// outbound request, in addition to whatever AuthMode contributes.
// Required for upstreams that demand a non-Authorization header on
// top of the OAuth bearer (Google's x-goog-user-project,
// vendor subscription keys, etc.). Operator-supplied; the model
// never sets or overrides these. Values are encrypted at rest.
StaticHeaders map[string]string
}
Config holds api-gateway toolkit configuration for a single upstream HTTP API connection.
func ParseConfig ¶
ParseConfig parses a Config from a generic map (the form admin-saved connections take in the connection_instances table) and applies defaults. The returned Config is fully validated.
type EndpointSchemaOutput ¶ added in v1.61.5
type EndpointSchemaOutput struct {
Spec string `json:"spec,omitempty"`
OperationID string `json:"operation_id"`
Method string `json:"method"`
Path string `json:"path"`
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
Parameters []ParameterDetail `json:"parameters,omitempty"`
RequestBody *RequestBodyDetail `json:"request_body,omitempty"`
Responses []ResponseDetail `json:"responses,omitempty"`
Examples map[string]any `json:"examples,omitempty"`
Note string `json:"note,omitempty"`
}
EndpointSchemaOutput is the structured response. Fields are omitted from JSON when empty so the typical "GET /things" operation doesn't waste context on absent request_body or examples.
type ExportAsset ¶
type ExportAsset struct {
ID string
OwnerID string
OwnerEmail string
Name string
Description string
ContentType string
S3Bucket string
S3Key string
SizeBytes int64
Tags []string
Provenance ExportProvenance
SessionID string
IdempotencyKey string
}
ExportAsset is the row inserted into portal_assets when an api_export call succeeds. Field shape mirrors trinokit.ExportAsset so the platform-side adapter can reuse its conversion logic.
type ExportAssetRef ¶
ExportAssetRef is returned by idempotency-key lookup. We only need the id + size for the response — the model doesn't see the existing asset's full row.
type ExportAssetStore ¶
type ExportAssetStore interface {
InsertExportAsset(ctx context.Context, asset ExportAsset) error
GetByIdempotencyKey(ctx context.Context, ownerID, key string) (*ExportAssetRef, error)
}
ExportAssetStore is the subset of portal.AssetStore needed by api_export. Defined locally to avoid an import cycle (portal → registry → apigateway). Mirrors trinokit.ExportAssetStore.
type ExportConfig ¶
ExportConfig holds platform-level limits for api_export. MaxBytes caps any single export's size (above which the call returns an error rather than a truncated asset — partial-data assets would be misleading). DefaultTimeout / MaxTimeout bound how long a single call may run.
type ExportDeps ¶
type ExportDeps struct {
AssetStore ExportAssetStore
VersionStore ExportVersionStore
S3Client ExportS3Client
S3Bucket string
S3Prefix string
BaseURL string
Config ExportConfig
GetUserContext func(ctx context.Context) *ExportUserContext
}
ExportDeps holds platform-side dependencies injected into the api gateway toolkit. All types are defined locally to avoid import cycles. Mirrors trinokit.ExportDeps so the platform-side wiring can stay symmetric.
type ExportProvenance ¶
type ExportProvenance struct {
ToolCalls []ExportProvenanceCall
SessionID string
UserID string
}
ExportProvenance captures the chain of tool calls that produced an asset so portal viewers can render "exported via api_export from <connection> <method> <path>".
type ExportProvenanceCall ¶
ExportProvenanceCall is one step in the provenance chain.
type ExportS3Client ¶
type ExportS3Client interface {
PutObject(ctx context.Context, bucket, key string, data []byte, contentType string) error
}
ExportS3Client is the subset of portal.S3Client needed by api_export. Note: this is the same shape as trinokit's; the platform adapter implementing it can serve both toolkits.
type ExportShareCreator ¶
type ExportShareCreator interface {
}
ExportShareCreator creates public share links for exported assets. nil disables public-link creation.
type ExportUserContext ¶
ExportUserContext holds user identity extracted from the request context. Populated by the GetUserContext callback provided in ExportDeps so the toolkit doesn't import middleware directly.
type ExportVersion ¶
type ExportVersion struct {
ID string
AssetID string
S3Key string
S3Bucket string
ContentType string
SizeBytes int64
CreatedBy string
ChangeSummary string
}
ExportVersion is the row inserted into portal_asset_versions.
type ExportVersionStore ¶
type ExportVersionStore interface {
CreateExportVersion(ctx context.Context, version ExportVersion) (int, error)
}
ExportVersionStore is the subset of portal.VersionStore needed by api_export.
type GetEndpointSchemaInput ¶ added in v1.61.5
type GetEndpointSchemaInput struct {
Connection string `json:"connection"`
OperationID string `json:"operation_id"`
Spec string `json:"spec,omitempty"`
}
GetEndpointSchemaInput is the parsed argument shape for the api_get_endpoint_schema tool.
type InvokeInput ¶
type InvokeInput struct {
Connection string `json:"connection"`
Method string `json:"method"`
Path string `json:"path"`
Query map[string]any `json:"query_params,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
Body any `json:"body,omitempty"`
TimeoutSeconds int `json:"timeout_seconds,omitempty"`
}
InvokeInput is the parsed argument shape for api_invoke_endpoint. Field names match the JSON schema.
type InvokeOutput ¶
type InvokeOutput struct {
Status int `json:"status"`
Headers map[string][]string `json:"headers,omitempty"`
Body any `json:"body,omitempty"`
BodyTruncated bool `json:"body_truncated,omitempty"`
// Pagination is populated when the upstream response carries a
// recognizable cursor (RFC 5988 Link rel="next", @odata.nextLink,
// next_cursor, etc). The model uses this to decide whether to
// issue a follow-up call. The gateway does NOT auto-follow so
// each loop iteration stays observable in audit + conversation.
Pagination *PaginationInfo `json:"pagination,omitempty"`
// Hint surfaces operator-actionable advice to the model when the
// response itself can't carry it — most importantly the "use
// api_export instead" suggestion when the body exceeded
// max_response_bytes. Distinct from Error: Hint is informational,
// the call still succeeded.
Hint string `json:"hint,omitempty"`
DurationMs int64 `json:"duration_ms"`
Error string `json:"error,omitempty"`
}
InvokeOutput is the structured result returned to the model and to the REST gateway shim. Outcomes are distinguished at the MCP-result layer (see buildInvokeResult and issue #432):
- Upstream responded with anything (2xx-5xx): the gateway succeeded at proxying. Status carries the upstream HTTP code, Error is normally empty, and the wrapping CallToolResult has IsError=false. Wire-level HTTP status from the REST shim stays 200; HTTP clients read the upstream code from Status and branch on it.
- Upstream responded with a status but the body could not be read in full (mid-stream drop, body exceeded the buffer allocator): Status carries the upstream code, Error carries the read-failure text, IsError stays false. The partial body is still returned in Body so callers can inspect what arrived.
- Gateway could not reach upstream OR the upstream call timed out: gateway-level failure. Status is 0, Error carries the scrubbed transport-error text, the wrapping CallToolResult has IsError=true, and the REST shim maps this to wire 502 (transport) or 504 (timeout).
type ListEndpointsInput ¶
type ListEndpointsInput struct {
Connection string `json:"connection"`
Query string `json:"query,omitempty"`
Limit int `json:"limit,omitempty"`
Ranking string `json:"ranking,omitempty"`
Spec string `json:"spec,omitempty"`
}
ListEndpointsInput is the parsed argument shape for api_list_endpoints. Field names match the JSON schema.
Spec restricts results to one component spec inside the connection's catalog. For a multi-spec catalog (e.g. a vendor shipping nine component specs under one connection) this is the per-section filter operators reach for once the catalog passes the size at which substring search across all 300+ operations stops being useful. Spec values come from the spec field on each operation in a prior api_list_endpoints response, so the model can pass them back verbatim.
type ListEndpointsOutput ¶
type ListEndpointsOutput struct {
Operations []OperationSummary `json:"operations"`
Note string `json:"note,omitempty"`
}
ListEndpointsOutput is the structured result. Empty + Note when the connection has no OpenAPI spec configured (so the model can distinguish "no spec" from "no matches").
type MultiConfig ¶
MultiConfig holds parsed per-connection configs plus the aggregate toolkit's default connection name.
func ParseMultiConfig ¶
ParseMultiConfig validates and returns the parsed config for every instance. Per-instance parse errors fail the platform startup; HTTP connectivity failures are handled at invocation time, not here.
type OAuth2Config ¶
type OAuth2Config struct {
// TokenURL is the upstream's token endpoint. Required.
TokenURL string
// ClientID is the platform's registered client id. Required.
ClientID string
// ClientSecret is the platform's registered client secret.
// Required. Encrypted at rest via the platform's
// FieldEncryptor (sensitive-key list already includes
// "client_secret"; the nested map's value is encrypted before
// storage in connection_instances.config).
ClientSecret string
// Scopes is an optional list of OAuth scopes to request.
Scopes []string
// EndpointAuthStyle controls how the client credentials are
// transmitted at token-fetch time. "header" (default) sends
// them as HTTP Basic auth on the token request; "params"
// sends them as POST body parameters. Some IdPs require one
// or the other; "header" is the OAuth 2.1 default.
EndpointAuthStyle string
// AuthorizationURL is the upstream's authorization endpoint.
// Required only for the authorization_code grant — that's
// where the platform redirects the admin's browser to start
// the flow.
AuthorizationURL string
// Prompt is an optional OIDC prompt parameter (RFC OIDC
// §3.1.2.1). Common values: "login" (force credential prompt),
// "consent" (force consent screen), "select_account",
// "none" (silent auth). Empty by default — the IdP decides.
// Operators of strict OIDC realms (Keycloak, Auth0, Okta)
// typically set this to "login" so admin Reconnect actions
// always re-prompt the user. Pure-OAuth (non-OIDC) providers
// often reject unknown parameters with invalid_request, so
// leave empty for those.
Prompt string
}
OAuth2Config describes the OAuth 2.1 client_credentials grant parameters. The platform exchanges ClientID + ClientSecret at TokenURL for an access token (cached + refreshed by the golang.org/x/oauth2 library) and applies it as "Authorization: Bearer <token>" on outbound calls.
Authorization-code (browser-driven, refresh-token-persisting) grants are deferred to a follow-up — they require DB state (PKCE verifier table, refresh-token cache) and an admin reauth callback handler that this PR intentionally does not bring in.
type OAuthKindHandler ¶ added in v1.60.1
type OAuthKindHandler struct{}
OAuthKindHandler adapts the HTTP API gateway toolkit to the unified connoauth flow. The admin layer's unified OAuth handler dispatches on the connection_kind path parameter ("api" for HTTP API gateway connections) and invokes this implementation for config extraction and post-auth side effects.
AfterConnect is a no-op for this kind: the toolkit's Authenticator reads the persisted token from the store lazily on every outbound request, so once the token is persisted the connection is immediately usable without further toolkit-side work.
func NewOAuthKindHandler ¶ added in v1.60.1
func NewOAuthKindHandler(_ *Toolkit) *OAuthKindHandler
NewOAuthKindHandler returns the API gateway adapter. The toolkit argument is accepted for symmetry with the MCP gateway adapter but is intentionally unused — the API gateway needs no post-auth side effect.
func (*OAuthKindHandler) AfterConnect ¶ added in v1.60.1
AfterConnect is a no-op. The API gateway's Authenticator reads the persisted token from the store on every outbound request via connoauth.Source, so once the token is persisted by the unified callback handler the connection is immediately usable. The MCP gateway, in contrast, caches an in-memory client per connection and must rebuild it after Connect; that's the MCP-side reason AfterConnect exists on the interface at all.
func (*OAuthKindHandler) ParseOAuthConfig ¶ added in v1.60.1
ParseOAuthConfig validates the connection's stored config and maps the HTTP API gateway's per-kind OAuth shape into a connoauth.Config. Returns an error when the connection is not configured for the authorization_code grant — the unified handler maps that to HTTP 409 Conflict, matching the prior per-kind handler's response code.
type OperationSummary ¶
type OperationSummary struct {
OperationID string `json:"operation_id"`
Method string `json:"method"`
Path string `json:"path"`
Summary string `json:"summary,omitempty"`
Tags []string `json:"tags,omitempty"`
Spec string `json:"spec,omitempty"`
}
OperationSummary is the slim per-operation view returned by api_list_endpoints. Designed to be cheap on context: the model gets enough to decide whether an operation is relevant (operation_id, method, path, summary, tags) without paying for the full request / response schema. Per-endpoint detail is fetched on demand via api_get_endpoint_schema.
Spec names the component spec inside the connection's catalog (e.g. "users", "orders"). Omitted from JSON when empty so connections with no catalog or a single anonymous spec stay slim on context.
type PaginationInfo ¶
type PaginationInfo struct {
HasMore bool `json:"has_more,omitempty"`
NextCursor string `json:"next_cursor,omitempty"`
NextURL string `json:"next_url,omitempty"`
Source string `json:"source,omitempty"`
}
PaginationInfo is the structured pagination state api_invoke_endpoint surfaces to the model on every response. The model uses HasMore + NextCursor (or NextURL) to decide whether to issue a follow-up call; the gateway does NOT auto-follow so each loop iteration stays observable in the conversation and audit log.
Fields are populated only when the upstream response carries a recognizable pagination signal. When none are populated the field is omitted from the JSON response — the model sees no pagination envelope and treats the response as terminal.
type ParameterDetail ¶ added in v1.61.5
type ParameterDetail struct {
Name string `json:"name"`
In string `json:"in"`
Required bool `json:"required,omitempty"`
Description string `json:"description,omitempty"`
Schema any `json:"schema,omitempty"`
}
ParameterDetail mirrors OpenAPI's parameter shape, stripped to the fields the model needs to construct a call.
type RankingMode ¶
type RankingMode string
RankingMode selects the algorithm api_list_endpoints uses to score candidate operations against the model's query.
Lexical (default) is the substring-match filter that v1 shipped: fast, deterministic, no embedding-provider dependency. Misses on natural-language queries when the model's phrasing doesn't share vocabulary with the spec author's (e.g. query "create order" vs summary "Place a new order").
Semantic uses cosine similarity between the query embedding and each operation's pre-computed embedding. Best for free-form intent queries; needs an embedding provider wired via SetEmbeddingProvider.
Hybrid blends a lexical signal (substring match) with the semantic cosine score. The blend recovers the precision of substring match for queries that DO share vocabulary while still returning semantically-related results when they don't. The blend weight (alpha) is fixed at hybridSemanticWeight; tuning is deferred to a config knob if a real-world deployment needs it.
const ( // RankingLexical is the v1 substring-match filter (default). RankingLexical RankingMode = "lexical" // RankingSemantic ranks by embedding cosine similarity only. RankingSemantic RankingMode = "semantic" // RankingHybrid blends a lexical signal with the cosine score. RankingHybrid RankingMode = "hybrid" )
RankingMode values exposed on the api_list_endpoints schema.
type RequestBodyDetail ¶ added in v1.61.5
type RequestBodyDetail struct {
Required bool `json:"required,omitempty"`
Description string `json:"description,omitempty"`
ContentTypes []string `json:"content_types,omitempty"`
Schema any `json:"schema,omitempty"`
Examples map[string]any `json:"examples,omitempty"`
}
RequestBodyDetail describes the request body shape.
type ResponseDetail ¶ added in v1.61.5
type ResponseDetail struct {
Status string `json:"status"`
Description string `json:"description,omitempty"`
ContentTypes []string `json:"content_types,omitempty"`
Schema any `json:"schema,omitempty"`
Examples map[string]any `json:"examples,omitempty"`
}
ResponseDetail describes one response status's shape.
type RoutePolicy ¶
type RoutePolicy interface {
Allow(ctx context.Context, connection, method, path string) (allowed bool, reason string)
}
RoutePolicy gates an api_invoke_endpoint call by (connection, method, path) on top of the platform's existing tool/connection authorization. Layered design: the MCP middleware's Authorizer.IsAuthorized check covers "may this user call api_invoke_endpoint at all?" and "on this connection at all?". RoutePolicy answers the more specific question "may this user call THIS method on THIS path of this connection?".
Reason is included for audit/log clarity when Allowed is false. Implementations must read the caller's roles from ctx (typically via the middleware's pre-authenticated user or an Authenticator) and resolve them to a persona's APIRoutes rules.
type Toolkit ¶
type Toolkit struct {
// contains filtered or unexported fields
}
Toolkit is the api-gateway toolkit. A single Toolkit manages multiple named connections, each addressing a different upstream HTTP API. Connections are added either at startup (from the platform's merged YAML+DB config) or at runtime via AddConnection (used by the admin REST handler when an operator saves a new connection through the portal).
func New ¶
New builds an empty toolkit. Connections are added later via AddConnection (used both by NewMulti at startup and by the admin hot-add path at runtime).
func NewMulti ¶
func NewMulti(cfg MultiConfig) *Toolkit
NewMulti builds a Toolkit and pre-loads the given parsed connection configs. Per-connection materialization failures are logged and skipped so a single bad connection does not block platform startup.
func (*Toolkit) AddConnection ¶
AddConnection parses a raw config map, materializes the per- connection auth + HTTP client, and registers the connection. Used both at startup (via NewMulti) and at runtime via the admin hot-add path.
func (*Toolkit) CatalogStore ¶ added in v1.61.5
CatalogStore returns the wired catalog store, or nil. Used by the admin layer to share the same store between toolkit reads and admin CRUD writes.
func (*Toolkit) ConnOAuthStore ¶ added in v1.61.3
ConnOAuthStore returns the unified OAuth token store wired into this toolkit, or nil when the toolkit was constructed without one.
func (*Toolkit) Connection ¶
Connection returns the default connection name for audit attribution when a tool call does not carry one. Empty string when no default is configured (multi-connection deployments typically require the model to pass `connection` explicitly).
func (*Toolkit) EmbeddingProvider ¶ added in v1.61.11
EmbeddingProvider returns the embedding provider configured on the toolkit, or nil. Exposed so the admin handler can fall back to the toolkit-wired provider when no platform-level embedder was injected via Deps.Embedder.
func (*Toolkit) HasConnection ¶
HasConnection reports whether a connection with the given name is registered.
func (*Toolkit) ListConnections ¶
func (t *Toolkit) ListConnections() []toolkit.ConnectionDetail
ListConnections returns details for every registered connection, in name-sorted order. Implements toolkit.ConnectionLister so the platform's unified list_connections tool surfaces api connections alongside trino, s3, and mcp.
func (*Toolkit) RegisterTools ¶
RegisterTools registers the api gateway's MCP tools.
func (*Toolkit) ReloadConnection ¶ added in v1.61.5
ReloadConnection drops and rebuilds the named connection so a catalog mutation (a portal save against api_catalog_specs) is reflected immediately. The connection's auth state and HTTP client are reconstructed from cfg; in-flight OAuth refresh tokens persist via the unified connoauth store.
Returns ErrConnectionNotFound when no connection has the given name. Other errors propagate from the rebuild path (config parse, authenticator construction).
func (*Toolkit) ReloadConnectionsByCatalog ¶ added in v1.61.5
ReloadConnectionsByCatalog rebuilds every registered connection whose CatalogID matches catalogID. Errors from individual rebuilds are logged but do not abort the sweep — one broken connection should not prevent the rest of the catalog's connections from picking up the new spec content.
func (*Toolkit) RemoveConnection ¶
RemoveConnection drops a registered connection. Used by the admin hot-remove path when an operator deletes the connection in the portal. Idle keepalive sockets on the per-connection HTTP client are closed so they don't linger up to idleConnectionTimeout after the connection is gone.
func (*Toolkit) RoutePolicy ¶
func (t *Toolkit) RoutePolicy() RoutePolicy
RoutePolicy returns the currently installed route policy, or nil if none has been wired. Exposed so platform-side tests can verify that WireAPIGatewayRoutePolicy actually installed a policy and exercise it directly without spinning up a full MCP server.
func (*Toolkit) SetAuthEvents ¶ added in v1.61.0
func (t *Toolkit) SetAuthEvents(w *authevents.Writer)
SetAuthEvents wires the audit-event writer to the toolkit and into every already-materialized authorization_code authenticator. Called by the platform alongside SetTokenStore so every outbound refresh emits its lifecycle event. The writer itself is nil-safe — passing nil silences events at the toolkit level (e.g., dev with no DB).
func (*Toolkit) SetCatalogStore ¶ added in v1.61.5
SetCatalogStore wires the catalog store the toolkit consults when a connection's CatalogID is set. Passing nil disables catalog- backed specs — connections with CatalogID configured still register, but with zero ops on their list_endpoints surface.
func (*Toolkit) SetConnOAuthStore ¶ added in v1.61.3
SetConnOAuthStore wires the unified OAuth token store. Required for the authorization_code grant: the Authenticator reads through the store on every Apply and persists rotated refresh tokens back. Connections registered before SetConnOAuthStore is called will pick up the store immediately because the wire step re-threads every already-materialized authorization_code Authenticator.
func (*Toolkit) SetEmbeddingProvider ¶
SetEmbeddingProvider wires the embedding model used at ranking time to embed the agent's query. nil (the default) disables non-lexical ranking; calls that request it fall back to lexical with a note. The toolkit no longer warms per-operation vectors from this hook — those are computed at spec-upsert time by the admin handler and persisted in api_catalog_operation_embeddings, which makes them survive restarts and shared across every connection that mounts the same catalog. To recompute vectors for a catalog that was written before the provider was wired, the operator runs the re-embed admin endpoint or re-saves the spec; the toolkit then picks up the new vectors on the next ReloadConnection.
func (*Toolkit) SetExportDeps ¶
func (t *Toolkit) SetExportDeps(deps ExportDeps)
SetExportDeps wires platform-side dependencies for api_export. Calling with nil-AssetStore deps is treated as "export disabled": registerExportTool checks t.exportDeps and skips registration so the model doesn't see a tool that would always fail.
func (*Toolkit) SetMetrics ¶ added in v1.64.0
func (t *Toolkit) SetMetrics(m *observability.Metrics)
SetMetrics wires the observability recorder into the toolkit and retroactively instruments every already-registered connection's HTTP client so connections added before metrics were enabled still emit outbound observations. Passing nil is supported and clears the recorder, but does not unwrap already-wrapped transports — rebuilding a connection (ReloadConnection / RemoveConnection + AddConnection) is the supported path to drop the wrapping.
Call this at startup, before traffic begins. The retro-wrap path mutates each connection's http.Client.Transport in place; http.Client does not document Transport as safe for concurrent reassignment with in-flight Do() calls. The platform's WireAPIGatewayMetrics is invoked once in cmd/main.go before any MCP listener starts accepting requests, so the in-place mutation is race-free in practice. instrumentClient is idempotent against the same (connection, metrics) pair so a second SetMetrics call with the same recorder is a no-op rather than a double-wrap.
func (*Toolkit) SetQueryProvider ¶
SetQueryProvider stores the query provider. Reserved for future warehouse-bridging features (see issue #372); not consumed in v1.
func (*Toolkit) SetRoutePolicy ¶
func (t *Toolkit) SetRoutePolicy(p RoutePolicy)
SetRoutePolicy installs a per-(connection, method, path) authorization gate. When set, api_invoke_endpoint consults the policy after the connection lookup and before the upstream call. A nil policy means no per-route gating — the platform's existing tool/connection authorization is the sole gate (backward-compatible).
func (*Toolkit) SetSemanticProvider ¶
SetSemanticProvider stores the semantic provider. Reserved for future enrichment (e.g. response shaping driven by DataHub PII tags, see issue #373); not consumed in v1.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package catalog models versioned, globally-owned OpenAPI spec bundles that api-gateway connections reference.
|
Package catalog models versioned, globally-owned OpenAPI spec bundles that api-gateway connections reference. |
|
Package embedjobs is the Postgres-backed embedding job queue for api-catalog operation vectors.
|
Package embedjobs is the Postgres-backed embedding job queue for api-catalog operation vectors. |