Documentation
¶
Overview ¶
Package mcpdiscovery holds the database-backed MCP discovery stores a deployment hands the manager (schemas.MCPConfig): SharedStore for clients without per_user_discovery and PerUserStore for clients with it.
Index ¶
- type PerUserStore
- func (s *PerUserStore) DeleteDiscovery(ctx context.Context, client *schemas.MCPClientConfig) error
- func (s *PerUserStore) EvictDiscoveriesByMCPClient(mcpClientID string)
- func (s *PerUserStore) EvictDiscoveriesByUser(userID string)
- func (s *PerUserStore) EvictDiscoveriesByVirtualKey(virtualKeyID string)
- func (s *PerUserStore) EvictDiscovery(mode schemas.MCPAuthMode, identity, mcpClientID string)
- func (s *PerUserStore) FlushDiscoveryCache()
- func (s *PerUserStore) GetDiscovery(ctx context.Context, client *schemas.MCPClientConfig) *schemas.MCPDiscovery
- func (s *PerUserStore) SetDiscovery(ctx context.Context, client *schemas.MCPClientConfig, ...) error
- func (s *PerUserStore) SetOnDiscoveryWritten(...)
- type SharedStore
- func (s *SharedStore) DeleteDiscovery(_ context.Context, client *schemas.MCPClientConfig) error
- func (s *SharedStore) GetDiscovery(_ context.Context, client *schemas.MCPClientConfig) *schemas.MCPDiscovery
- func (s *SharedStore) SetDiscovery(_ context.Context, client *schemas.MCPClientConfig, ...) error
- func (s *SharedStore) SetWriteGate(allow func() bool)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type PerUserStore ¶
type PerUserStore struct {
// contains filtered or unexported fields
}
PerUserStore keeps the discoveries of MCP clients with per_user_discovery on: one per caller (a user or a virtual key) and client, persisted in mcp_per_user_discoveries, which is the source of truth, and served from memory. Writes go to the database before they change what this process serves.
The caller is resolved off ctx exactly as the per-user credential lookup resolves it, so a discovery written with the ctx its credential was looked up with lands under that credential's identity.
Each process keeps two things per client: the set of callers that hold a discovery on it, with each one's discovery hash, loaded from the database the first time the client is read; and a bounded cache of the distinct discoveries those callers hold, keyed by (client, hash), so callers holding the same discovery share one copy. A caller outside the set has no discovery and costs no database read, and a member is served the copy cached under the hash the set records for it. The set changes on this process's own writes and deletes, on another process's (delivered through EvictDiscovery), and is reloaded from the database once it is older than membershipRefreshInterval, which corrects a process that missed one. A cached copy no member's hash names any more is never served again, and ages out of the cache.
All methods are safe on a nil receiver: reads miss and writes are refused.
func NewPerUserStore ¶
func NewPerUserStore(configStore configstore.ConfigStore, logger schemas.Logger) *PerUserStore
NewPerUserStore returns a PerUserStore reading and writing through configStore. A nil configStore leaves every caller with no per-user discovery.
func (*PerUserStore) DeleteDiscovery ¶
func (s *PerUserStore) DeleteDiscovery(ctx context.Context, client *schemas.MCPClientConfig) error
DeleteDiscovery removes the caller's discovery for client. A ctx with no user or virtual-key caller is refused before any I/O.
func (*PerUserStore) EvictDiscoveriesByMCPClient ¶
func (s *PerUserStore) EvictDiscoveriesByMCPClient(mcpClientID string)
EvictDiscoveriesByMCPClient forgets one client: its set and every cached discovery of it. Its next read loads it again. Used when the client is removed, or every discovery of it is.
func (*PerUserStore) EvictDiscoveriesByUser ¶
func (s *PerUserStore) EvictDiscoveriesByUser(userID string)
EvictDiscoveriesByUser makes every user-mode discovery of one user, across clients, read again, the same way as EvictDiscoveriesByVirtualKey.
func (*PerUserStore) EvictDiscoveriesByVirtualKey ¶
func (s *PerUserStore) EvictDiscoveriesByVirtualKey(virtualKeyID string)
EvictDiscoveriesByVirtualKey makes every vk-mode discovery of one virtual key, across clients, read again: its next read of each checks the database. A deleted key's rows are gone, and a key that only changed keeps its discoveries. See forgetHashes.
func (*PerUserStore) EvictDiscovery ¶
func (s *PerUserStore) EvictDiscovery(mode schemas.MCPAuthMode, identity, mcpClientID string)
EvictDiscovery applies another process's write or delete of one binding: the caller is kept in the client's set with no hash, so its next read checks the database. Deletes are not taken out of the set directly: messages from two processes can arrive in another order than their writes landed, and a delete applied after a later write would hide a discovery the database has until the next refresh. Cached copies stay for the other callers holding them.
func (*PerUserStore) FlushDiscoveryCache ¶
func (s *PerUserStore) FlushDiscoveryCache()
FlushDiscoveryCache forgets every client and every cached discovery.
func (*PerUserStore) GetDiscovery ¶
func (s *PerUserStore) GetDiscovery(ctx context.Context, client *schemas.MCPClientConfig) *schemas.MCPDiscovery
GetDiscovery returns the caller's discovery for client, with tool names prefixed by the client's current name. A caller outside the client's set gets nil without a database read. Callers holding the same discovery are served one cached copy of it (see discoveryOf). A read error is logged and answered with nil, so the caller sees no tools rather than someone else's.
func (*PerUserStore) SetDiscovery ¶
func (s *PerUserStore) SetDiscovery(ctx context.Context, client *schemas.MCPClientConfig, discovery *schemas.MCPDiscovery) error
SetDiscovery replaces the caller's discovery for client. Tool names are stored without the client prefix. A ctx with no user or virtual-key caller is refused before any I/O.
func (*PerUserStore) SetOnDiscoveryWritten ¶
func (s *PerUserStore) SetOnDiscoveryWritten(cb func(ctx context.Context, mode schemas.MCPAuthMode, identity, mcpClientID string))
SetOnDiscoveryWritten registers a callback run after this store writes or deletes a discovery, once this process's own copy reflects it. That reaches only this process; a deployment running several processes over one database uses the callback to have the others apply EvictDiscovery too. The callback runs inline on the write path, so it must return promptly. nil clears it.
type SharedStore ¶
type SharedStore struct {
// contains filtered or unexported fields
}
SharedStore keeps the discoveries of MCP clients without per_user_discovery: one per client, served to every caller from memory, which is the source of truth, and written to the client's row so a restart and the admin API find them.
The manager calls it while holding its own lock, so nothing here waits on the database. SetDiscovery records the discovery and hands the write to a background writer, one per client, which writes the latest discovery it has been handed, skips one whose content the row already holds, and retries a failed write. A discovery with no DiscoveredAt was not produced by an upstream tools/list (the manager restored it from the row), so it is taken as what the row holds: it is not written, and a write handed over before it is dropped.
All methods are safe on a nil receiver: reads miss and writes are refused.
func NewSharedStore ¶
func NewSharedStore(configStore configstore.ConfigStore, logger schemas.Logger) *SharedStore
NewSharedStore returns a SharedStore writing through configStore. A nil configStore keeps discoveries in memory only.
func (*SharedStore) DeleteDiscovery ¶
func (s *SharedStore) DeleteDiscovery(_ context.Context, client *schemas.MCPClientConfig) error
DeleteDiscovery forgets client's discovery; ctx is ignored. The row is left as it is: a disabled client is restored from it when re-enabled, and a removed client's row goes with the client. A write already handed to the writer still lands, but is not retried once it fails.
func (*SharedStore) GetDiscovery ¶
func (s *SharedStore) GetDiscovery(_ context.Context, client *schemas.MCPClientConfig) *schemas.MCPDiscovery
GetDiscovery returns client's discovery; ctx is ignored.
func (*SharedStore) SetDiscovery ¶
func (s *SharedStore) SetDiscovery(_ context.Context, client *schemas.MCPClientConfig, discovery *schemas.MCPDiscovery) error
SetDiscovery replaces client's discovery and, unless it was restored, queues it to be written to the client's row; ctx is ignored. It never waits on the database.
func (*SharedStore) SetWriteGate ¶
func (s *SharedStore) SetWriteGate(allow func() bool)
SetWriteGate registers allow, asked before every write to a client's row; a write it refuses is skipped, and the client's next discovery tries again. Every process discovers the same tools for a shared client, so a deployment running several processes over one database can leave the writes to one of them. nil clears it.