Documentation
¶
Overview ¶
Package portal provides the MCP toolkit for saving and managing AI-generated assets (JSX dashboards, HTML reports, SVG charts).
Index ¶
- Constants
- type Config
- type DirectoryReader
- type RegisterOptions
- type ResourceHoldReader
- type ResourceHolds
- type ResourceWriter
- type TableRegistrar
- type TableRegistration
- type Toolkit
- func (*Toolkit) Close() error
- func (*Toolkit) Connection() string
- func (t *Toolkit) FollowAssetTables(ctx context.Context, assetID string, version int) []string
- func (t *Toolkit) FollowResourceTables(ctx context.Context, resourceID string, version int) []string
- func (*Toolkit) Kind() string
- func (t *Toolkit) LinkInsight(ctx context.Context, threadIDs []string, insightID, actorID, actorEmail string) ([]string, error)
- func (t *Toolkit) Name() string
- func (*Toolkit) PromptInfos() []registry.PromptInfo
- func (t *Toolkit) RegisterTools(s *mcp.Server)
- func (t *Toolkit) SetContentRefs(d *assetrefs.Declarer)
- func (t *Toolkit) SetFeedbackNotifications(notifier portal.Notifier, mentions portal.MentionResolver)
- func (t *Toolkit) SetQueryProvider(provider query.Provider)
- func (t *Toolkit) SetResourceHolds(r ResourceHoldReader)
- func (t *Toolkit) SetResourceWriter(w ResourceWriter)
- func (t *Toolkit) SetSemanticProvider(provider semantic.Provider)
- func (t *Toolkit) SetTableRegistrar(reg TableRegistrar)
- func (*Toolkit) Tools() []string
Constants ¶
const ( // SaveToolName is the name of the asset-save tool. SaveToolName = "save_asset" // ManageToolName is the name of the asset-management tool. ManageToolName = "manage_asset" // ManageTableToolName is the name of the table-registration tool. It is // not asset-keyed: it acts on any stored file a reference names, which is // why it is its own tool rather than a set of manage_asset actions. ManageTableToolName = "manage_table" // ManageResourceToolName is the name of the managed-resource write tool // (#1487). Like manage_table it is not asset-keyed: a managed resource is // its own kind of record, with its own scopes and its own version trail. ManageResourceToolName = "manage_resource" )
Tool names registered by the portal toolkit. Exported because pkg/platform needs the save tool's name to configure provenance harvesting.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
Name string
AssetStore portal.AssetStore
VersionStore portal.VersionStore
CollectionStore portal.CollectionStore
ThreadStore portal.ThreadStore
S3Client portal.S3Client
S3Bucket string
S3Prefix string
BaseURL string
MaxContentSize int // max asset content size in bytes (0 = no limit)
// Embedder embeds search queries for the ranked `search` action. When nil
// or the noop placeholder, search degrades to lexical-only ranking (the
// store decides via embedding.EmbedForSearch).
Embedder embedding.Provider
// CaptureProvenance resolves which calls an asset write was built from
// (#1320). Injected by the platform, which owns the audit log the capture
// is read from. Nil leaves an asset recording its session and owner but no
// calls — what a deployment without audit gets.
CaptureProvenance portal.ProvenanceCapturer
// Directory resolves a person's name to their address for the share
// action (#1280). Nil (no database, so no directory) leaves sharing
// working by email address only.
Directory DirectoryReader
}
Config holds configuration for creating a portal toolkit.
type DirectoryReader ¶ added in v1.121.0
type DirectoryReader interface {
List(ctx context.Context, filter user.Filter) ([]user.User, int, error)
}
DirectoryReader looks people up in the known-users directory. The share action only reads it, so this is the whole surface it takes, the same view the portal's share picker takes over the same store; pkg/user.Store implements it.
type RegisterOptions ¶ added in v1.126.5
type RegisterOptions struct {
// Repair asks for a corrected version of the file to be saved and
// registered when the file cannot be read as a table the way it is stored;
// without it such a file is refused and the refusal says what is wrong
// with it.
Repair bool
// Follow moves the table onto each new revision or version of the file
// as it is written; off, the table is pinned to the version it was
// registered over. It is the resolved choice: the tool defaults it to on
// when the caller says nothing.
Follow bool
}
RegisterOptions are the choices a registration is made with.
type ResourceHoldReader ¶ added in v1.131.0
type ResourceHoldReader interface {
ResourceHolds(ctx context.Context, resourceID string) (ResourceHolds, error)
}
ResourceHoldReader answers what still points at a managed resource, so a delete can say what it would break before it breaks it.
It is a capability the toolkit asks for rather than one it implements: the two reverse lookups live in two different layers, and the toolkit must not learn what either of them is.
type ResourceHolds ¶ added in v1.131.0
type ResourceHolds struct {
Assets int `json:"assets"`
Prompts int `json:"prompts"`
// More says a count was cut at the bound rather than being the whole of
// what points at the file. It is reported rather than left implicit
// because a short count read as a complete one is the mistake this answer
// exists to prevent.
More bool `json:"more,omitempty"`
}
ResourceHolds counts the records still pointing at a managed resource (#1665): assets whose content references it, and prompts that attach it as reference material.
Neither is a foreign key -- deleting the file leaves the row behind so the thing that depended on it reports the material as missing -- which is why the count has to be gathered and put to whoever is deleting, before the delete rather than after it.
A knowledge page is deliberately not counted: the platform refuses an mcp:resource: citation on a shared page, because a resource is visibility-scoped, so no page can point at one.
It is counts and not names because each of those records carries an audience of its own and the person deleting the file is not necessarily in any of them. The portal's used-by panel resolves those audiences and names what its reader may open; this is the part that can be said to anybody who can see the file.
func (ResourceHolds) Any ¶ added in v1.131.0
func (h ResourceHolds) Any() bool
Any reports whether anything at all points at the file.
func (ResourceHolds) Describe ¶ added in v1.131.0
func (h ResourceHolds) Describe() []string
Describe renders the counts as the phrases a refusal lists, one per kind that has anything, in the order a reader cares about them: what serves the file to people, then what a procedure depends on.
type ResourceWriter ¶ added in v1.126.0
type ResourceWriter interface {
// Create files new content as a managed resource, refusing a scope the
// caller may not write to.
Create(ctx context.Context, in resource.NewResource, claims resource.Claims) (*resource.Resource, error)
// Replace records new content as the resource's next revision, keeping the
// id, the canonical URI and the filename, and returns the version number
// the content was recorded as.
Replace(
ctx context.Context, id string, up resource.RevisionUpload, claims resource.Claims,
) (*resource.Resource, int, error)
// Get reads a resource the caller may see. A replacement resolves the file
// before it decodes anything, so a reference naming a file that is not
// there is refused without the payload being read, and so type detection
// is given the stored filename rather than a name the caller offered --
// the filename is what a replacement must not change.
Get(ctx context.Context, id string, claims resource.Claims) (*resource.Resource, error)
// Locate reads the resource filed at an address, and reports the canonical
// mcp:// URI that address names whether or not anything is filed there
// (#1665). Nothing being there is an answer rather than an error: it is
// what a create-or-replace decides on, and what a lookup of an empty
// address reports.
Locate(
ctx context.Context, addr toolkit.ResourceAddress, claims resource.Claims,
) (found *resource.Resource, uri string, err error)
// List returns the resources filed under a folder that the caller may see,
// newest first, with the total the page was cut from.
List(
ctx context.Context, q toolkit.ResourceQuery, claims resource.Claims,
) (found []resource.Resource, total int, err error)
// Delete removes a resource and the objects its content lives in,
// returning the record that was removed. The authority is the authority to
// change the file, the same rule replacing its content meets.
Delete(ctx context.Context, id string, claims resource.Claims) (*resource.Resource, error)
}
ResourceWriter writes a managed resource on an agent's behalf (#1487).
A managed resource is the only kind of file an asset can reference, so without a writer the data half of a referencing asset can only be refreshed by a person at an upload form. This is the capability that makes an asset's numbers refreshable by the platform itself.
The acting caller is not a parameter, the same way it is not one on TableRegistrar: both methods take the resource claims this toolkit derives from the call's own identity, so the tool cannot present an identity the REST surface would refuse, and the scope boundary a tool call meets is the one an upload meets.
type TableRegistrar ¶ added in v1.125.0
type TableRegistrar interface {
// Register makes the referenced file queryable, under the options the
// caller chose for the registration.
Register(ctx context.Context, reference, connection, tableName string, opts RegisterOptions) (*TableRegistration, error)
Unregister(ctx context.Context, registrationID string) error
Tables(ctx context.Context, reference string) ([]TableRegistration, error)
// DropAssetTables removes every table registered over an asset. It is what
// a delete calls: the asset is going, and a table over where its file used
// to be would answer queries from a schema its owner can no longer see.
// Best-effort by contract -- a delete must not fail because a scratch table
// could not be dropped.
DropAssetTables(ctx context.Context, assetID string)
// DropResourceTables is DropAssetTables for a deleted managed resource
// (#1665). The REST delete route already reaches this through the store's
// own delete hook; a tool call does not cross that route, so the toolkit
// calls it itself and both doors leave the same state behind.
DropResourceTables(ctx context.Context, resourceID string)
// FollowAssetTables and FollowResourceTables are what a write of a new
// version calls (#1536): every following registration over the file is
// moved onto the version written, and every registration over it is
// reported -- followed, pinned and now behind, or not movable and why --
// as the sentences the write's result carries. Best-effort by contract,
// like the delete: the file changed, and that write succeeded whatever
// happened to the tables over it.
FollowAssetTables(ctx context.Context, assetID string, version int) []string
FollowResourceTables(ctx context.Context, resourceID string, version int) []string
}
TableRegistrar makes a stored CSV readable as a query-engine table (#1327), whichever kind of stored file it is (#1428). The registrar seam satisfies it; the capability is declared here so the toolkit does not depend on the registrar, and does not learn what a managed resource is.
Every method is keyed by the canonical reference a caller already holds -- the string `search` emits and `fetch` dereferences -- rather than by the id of one kind of record. That is what lets one tool serve every kind: the kind travels inside the reference, so there is no per-kind argument and no second tool.
The acting caller is not a parameter. Every method resolves identity from the context exactly as this toolkit does, so the tool cannot present an identity the rest of the platform would refuse, and the connection boundary a tool call meets is the one a registration meets.
type TableRegistration ¶ added in v1.125.0
type TableRegistration struct {
RegistrationID string `json:"registration_id"`
Connection string `json:"connection"`
QueryTable string `json:"query_table"`
Columns []string `json:"columns,omitempty"`
SampleSQL string `json:"sample_sql,omitempty"`
RegisteredBy string `json:"registered_by,omitempty"`
// Stale means the file has a newer revision or version than the one the
// table points at, so the rows are the content that was current when it
// was registered.
Stale bool `json:"stale"`
// Follow means the table is moved onto each new revision or version of
// the file as it is written (#1536); without it the table is pinned to
// the version it was registered over. FollowError is why the last follow
// did not move it, empty while the table is where the file is.
Follow bool `json:"follow"`
FollowError string `json:"follow_error,omitempty"`
// Repair means the table corrects its file: a new version carrying a
// defect a reader cannot see past that the platform can correct is saved
// corrected, as the file's next version, and the table is moved onto that
// version (#1577). It is the choice made when the table was registered.
Repair bool `json:"repair"`
// Repaired says what a correction of the file changed before it could be
// registered, and is empty when none was needed (#1441). The file itself
// changed, so the person who asked for the registration is told so.
Repaired string `json:"repaired,omitempty"`
// TableChanges is what a replacing registration found about the OTHER
// tables on the connection after its DROP ran (#1546): one sentence per
// table that no longer exists. Empty when every other table is still
// there.
//
// It is a change report, not an inventory, and it is named apart from the
// `tables` a reader queries for that reason (#1666).
TableChanges []string `json:"table_changes,omitempty"`
}
TableRegistration is one registration as the tool reports it.
type Toolkit ¶
type Toolkit struct {
// contains filtered or unexported fields
}
Toolkit implements the portal asset toolkit.
func (*Toolkit) Connection ¶
Connection returns the connection name for audit logging.
func (*Toolkit) FollowAssetTables ¶ added in v1.126.5
FollowAssetTables reports what a new version of an asset did to the tables registered over it. A deployment with no registrar has none.
It is exported for the composition root, which hands it to the script runner: a script's platform.export writes a version through its own store rather than through this toolkit, and reaches the registrar through the toolkit because the registrar is bound here after the runner is built.
func (*Toolkit) FollowResourceTables ¶ added in v1.131.0
func (t *Toolkit) FollowResourceTables(ctx context.Context, resourceID string, version int) []string
FollowResourceTables is FollowAssetTables for a replaced managed resource. It is exported for the same reason: a write path assembled before the registrar exists reaches it through the toolkit the registrar is bound onto. An export landing in a managed resource is such a path (#1663).
func (*Toolkit) LinkInsight ¶ added in v1.84.0
func (t *Toolkit) LinkInsight(ctx context.Context, threadIDs []string, insightID, actorID, actorEmail string) ([]string, error)
LinkInsight implements the knowledge ThreadLinker bridge with authorization. capture_insight calls this with the thread_ids an insight resolves. The agent surface must not be able to resolve a thread it could not resolve through resolve_thread, so each thread is gated through callerCanActOnThread (the same owns-or-edit / author / admin policy) using the caller identity in ctx. Threads the caller may not moderate, that are missing, empty, or duplicated are skipped and surface to the agent as unlinked_thread_ids. Authorized ids are delegated to the thread store, which performs the link transactionally.
func (*Toolkit) PromptInfos ¶ added in v1.38.0
func (*Toolkit) PromptInfos() []registry.PromptInfo
PromptInfos returns metadata for prompts registered by the portal toolkit.
func (*Toolkit) RegisterTools ¶
RegisterTools registers save_asset, manage_asset, manage_table, manage_resource and manage_feedback with the MCP server.
func (*Toolkit) SetContentRefs ¶ added in v1.126.0
SetContentRefs binds the path that declares what an asset's content references (#1474, #1488).
It is installed after construction, like the feedback notifier below, because the reference store and the asset store are assembled with the portal layer. Left unbound, the `references` argument is refused with that explanation rather than accepted and silently dropped.
func (*Toolkit) SetFeedbackNotifications ¶ added in v1.114.0
func (t *Toolkit) SetFeedbackNotifications(notifier portal.Notifier, mentions portal.MentionResolver)
SetFeedbackNotifications installs the notification trigger and the mention resolver for agent-authored feedback replies. The composition root calls it once the notification substrate exists, which is later than toolkit construction; like the provider setters it must be called during composition, before the server starts serving.
That substrate (queue, send worker, SMTP) is owned by the HTTP composition root, so a stdio deployment leaves both nil: an agent's reply is stored and its mentions parsed, but nothing is mailed and no mention is recorded. The send worker belongs to a long-lived server rather than to a per-client stdio process, where every concurrent client would run its own.
func (*Toolkit) SetQueryProvider ¶
SetQueryProvider sets the query execution provider.
func (*Toolkit) SetResourceHolds ¶ added in v1.131.0
func (t *Toolkit) SetResourceHolds(r ResourceHoldReader)
SetResourceHolds binds the reader behind the delete warning. Called by the composition root once the asset and prompt layers exist. Without it a delete cannot establish what depends on the file, and says so rather than reporting that nothing does.
func (*Toolkit) SetResourceWriter ¶ added in v1.126.0
func (t *Toolkit) SetResourceWriter(w ResourceWriter)
SetResourceWriter binds the writer behind manage_resource. Called by the composition root once the managed-resource layer exists, which is later than toolkit construction; without it the tool reports that the deployment has no managed-resource layer, which is what a deployment with no database or no blob storage can do.
func (*Toolkit) SetSemanticProvider ¶
SetSemanticProvider sets the semantic metadata provider.
func (*Toolkit) SetTableRegistrar ¶ added in v1.125.0
func (t *Toolkit) SetTableRegistrar(reg TableRegistrar)
SetTableRegistrar binds the registrar behind manage_table. Called by the composition root once the Trino toolkit and the registration store exist; without it the tool reports that the deployment cannot register tables, which is what a deployment with no Trino scratch connection can do.