README
¶
Scout
Scout is a shared Go foundation for building multi-tenant agent platforms. It defines provider-neutral domain types, application interfaces, reusable services, and a portable keel schema. Downstream applications supply product behavior, adapters, and deployable binaries.
Scout implements product-neutral behavior behind its public contracts, including reusable Studio HTTP handlers and MCP protocol adapters. It contains no deployable binary, product workflow, model provider, queue, cache, or frontend.
Scout is licensed under the Apache License 2.0.
Repository contract
| Path | Purpose |
|---|---|
api/ |
Versioned HTTP compatibility DTOs |
domain/ |
Provider-neutral values exchanged across platform boundaries |
contract/ |
Interfaces implemented or composed by downstream applications |
service/ |
Product-neutral implementations of Scout contracts |
handler/ |
HTTP-only Agent Studio compatibility adapter |
mcp/ |
MCP server, provider, envelope, resource, and conformance helpers |
schema/ |
Database-neutral table definitions compiled by keel |
STUDIO_API.md |
Agent Studio HTTP compatibility reference |
go.mod |
Module identity and the pinned keel schema compiler tool |
Scout owns agent-platform vocabulary and invariants. keel owns horizontal infrastructure.
| Concern | Owner |
|---|---|
| Agent definitions, Studio drafts, prompt inheritance, releases, execution graphs, turns, usage, tools, knowledge, guardrails | Scout domain and contracts |
| Product-specific behavior, prompt content, baseline selection, workflows, and approval rules | Downstream application |
| Database repository, named query service, transactions, ID generation | keel |
| HTTP server, authentication, authorization, envelopes, generic REST | keel |
Agent Studio lifecycle service, handler contract, and studio-v1 adapter |
Scout |
| Worker lifecycle, polling, leases, health endpoints | keel |
| MCP server, transports, protocol DTO projection, envelopes, and provider registration | Scout |
| MCP authentication, authorization, quota, client IP, secrets, and HTTP infrastructure | keel |
| MCP product definitions, caller context, and tool/resource/prompt operations | Scout domain and contracts |
| Flags, runtime configuration, secret providers, storage, cache | keel |
| Provider adapters and composition factories | Downstream application |
Do not add a Scout abstraction that duplicates a keel primitive. Add reusable horizontal behavior to keel; add agent-domain behavior to Scout; keep product behavior downstream.
Architecture
Use separate control and data planes even when an initial deployment runs them in one process.
- The control plane publishes immutable agent, tool, guardrail, knowledge, policy, and rollout versions.
- The data plane admits turns, reserves budget, dispatches durable work, restores state, executes pinned graphs, and returns guarded output.
- Model, embedding, retrieval, and tool access pass through governed application interfaces.
- Workers checkpoint durable state before acknowledging work or refreshing a cache.
- Agent-version canaries and platform-release rings remain independent.
Recommended deployable roles:
| Binary | Responsibility | Scale signal |
|---|---|---|
control-api |
Agent Studio administration and immutable publication | Administrative request rate |
conversation-api |
Authentication, admission, reply subscription, client streaming | Connections and admitted turns |
runtime-worker |
Fair turn execution and durable checkpoints | Queue depth and execution latency |
platform-worker |
Compilation, compatibility runs, rollout advancement | Pending control-plane jobs |
model-gateway |
Provider routing, quotas, capacity, output streaming | Tokens and provider latency |
tool-gateway |
Authorization, credentials, egress, retries, validation | Tool calls and dependency health |
mcp-server |
MCP tools/resources backed by application services | MCP sessions and calls |
A small installation may combine API roles, but background work remains in worker binaries. Never start durable or long-running goroutines from an HTTP handler.
Core invariants
- Every operation is tenant-scoped at the API, service, persistence, queue, cache, vector, and audit boundaries.
- Published definitions are immutable; edits create a new version.
- Draft and shared prompt edits use independent, explicit optimistic revisions.
- Runtime reads only immutable definitions selected through an alias and deployment.
- A conversation pins its agent, guardrail, tool, and knowledge versions.
- The durable session store is authoritative; cache loss affects latency, not correctness.
- Queue delivery is at least once; stable idempotency keys make step effects replay-safe.
- Raw model or tool output never reaches a client before guardrail approval.
- Model and tool credentials come from keel secret providers and never from agent definitions.
- Cost is stored as
int64minor units with an explicit currency and currency exponent. - Large inputs, outputs, state, and audit payloads live in object storage; relational rows store a URI and digest.
- Provider selection occurs in composition factories controlled by
flagvalues.
Interface map
The interfaces are deliberately smaller than a complete runtime. Implement only the contracts required by a downstream vertical slice.
| File | Main contracts | Downstream responsibility |
|---|---|---|
contract/control_plane.go |
AgentVersionRepository, AgentCompiler, AgentPublisher, registries, traffic manager |
Validate, compile, publish, and activate immutable definitions |
contract/studio.go |
Prompt compiler, baseline selector, draft validator/tester, kind/model catalogs, published resolver | Inject product rules and governed test execution |
contract/studio_http.go |
AgentStudioHTTPBackend |
Extend the shared service only where product behavior is required |
contract/mcp.go |
Server description, tool/resource/prompt operations, field catalog | Expose product capabilities through Scout MCP adapters |
contract/data_plane.go |
ConversationIngress, ConversationRuntime, dispatcher, scheduler, session, reply, step ports |
Admit and execute turns with replay and streaming |
contract/model_runtime.go |
Router, gateway, provider registry, stream, capacity | Govern provider selection and inference |
contract/tool_gateway.go |
Authorization, credentials, egress, transport, retry, circuit breaker | Govern every external tool effect |
contract/guardrail.go |
GuardrailEnforcer |
Apply pinned policy at model and tool boundaries |
contract/knowledge.go |
Ingestor, document store, embedding, vector index, retriever | Build and query immutable tenant knowledge versions |
contract/isolation.go |
Rate, budget, execution, loop, cost, concurrency controls | Enforce tenant and fleet limits |
contract/release_and_observability.go |
Compatibility runs, rollout, audit, runtime metrics | Protect platform releases and record outcomes |
contract/health.go |
HealthProbe |
Expose dependency readiness to composition code |
The domain/ package contains transport- and provider-neutral DTOs. Provider SDK types must not cross these interfaces.
Shared service implementations
The contract package remains flat so consumers use one stable import path. Concrete implementations are grouped by responsibility:
| Package | Implementations | Injected boundaries |
|---|---|---|
service/controlplane |
StudioService, KeelPromptSourceRepository, AgentPublisher, PromptCompiler, PromptDraftAssembler |
Keel database, baseline selection, product validation/testing, kind/model catalogs |
service/isolation |
ExecutionGovernor and per-turn execution permits |
Loop detection and cost circuit breaking |
service/modelgateway |
Gateway, ProviderRegistry, and lease-owning streams |
Rate limiting, capacity scheduling, and model providers |
service/release |
ContractTestRunner |
Governed test execution and assertion evaluation |
service/runtime |
PublishedAgentResolver, DefinitionResolver, SessionCoordinator, StepExecutorRegistry |
Keel database, durable stores, non-authoritative caches, metrics, and step executors |
service/toolgateway |
GovernedGateway and bounded RetryPolicy |
Tool registry, authorization, credentials, egress, circuit breaking, transport, and result validation |
These services enforce ordering and failure semantics but do not provide no-op infrastructure. Cache failures fall back to durable storage and are reported through RuntimeMetrics; durable writes complete before cache invalidation; model capacity is released on every unary or streaming terminal path; and tool calls cannot bypass authorization, egress, circuit, credential, or result validation boundaries.
Tests share focused fakes under service/internal/fake. Provider adapters, distributed queues, caches, secret stores, and product transports remain separate implementations behind injected contracts.
Agent Studio contract
Studio separates mutable authoring state from immutable runtime state:
agent_profileowns identity, logical kind, display name, and the operational kill switch.agent_draftowns revisioned model, approval, enabled, and product-extension settings.- Prompt compilation resolves platform baseline, tenant-kind default, and agent override rows.
agent_aliasmaps a logical tenant role to one named agent and carries the shared prompt-profile revision.- Publication freezes compiled prompts and provenance into canonical
agent_version.definitionJSON. agent_deploymentselects stable and optional canary versions for the aliased agent.
controlplane.StudioService implements AgentStudioHTTPBackend. It owns named SQL, prompt resolution, common validation, optimistic revisions, kill-switch updates, publication, restore, reset, history, release sections, and lifecycle audit. handler.StudioHandler authenticates through keel, derives domain.StudioActor, calls one backend method, and maps studio-v1 DTOs and errors.
Product applications implement PromptBaselineSelector, AgentDraftValidator, AgentDraftTestExecutor, AgentKindCatalog, and StudioModelCatalog. Scout owns the inheritance vocabulary and lifecycle contract but never hard-codes product agent kinds, prompt text, capability catalogs, or provider construction.
See STUDIO_API.md for the initial HTTP compatibility profile.
Shared prompt compiler
controlplane.PromptCompiler and controlplane.PromptDraftAssembler merge resolved prompt rows, combine source provenance with effective Studio content, produce immutable language snapshots, and create versioned SHA-256 digests without database or provider dependencies.
For each prompt section, the compiler applies these rules:
- The platform baseline is retained when present.
- The tenant default appends to the baseline.
- An agent override with
Overwriteremoves the tenant-default instruction but retains the baseline. - An agent override without
Overwriteappends after the other levels. - The most specific non-empty output contract wins.
- Sections are ordered by display order and then prompt-section id.
DefinitionDigest includes agent kind, model provider/model pairs, enabled and approval policy, canonical extension JSON, and sorted compiled-language digests. Identity, version, revision, publication, and release-note fields are excluded because they do not change runtime behavior. Stored language digests are verified before a definition digest is produced.
compiler := &controlplane.PromptCompiler{}
compiled, err := compiler.Compile(languageCode, resolved.Rows)
if err != nil {
return err
}
definition.Languages = append(definition.Languages, compiled)
definition.DefinitionDigest, err = compiler.DefinitionDigest(definition)
controlplane.KeelPromptSourceRepository implements PromptSourceRepository over prompt_baseline, tenant_prompt_default, and agent_prompt_override. The injected PromptBaselineSelector supplies ordered product keys; Scout selects the first matching baseline per section and never embeds product precedence.
runtime.PublishedAgentResolver resolves an active alias to its immutable stable or sticky canary definition, enforces the operational kill switch, and validates the requested language. Provider construction remains downstream.
MCP extension contract
Scout separates product MCP behavior from the MCP protocol implementation:
domain.MCPServerDefinition,MCPCaller, and the tool, resource, prompt, result, evidence, and task-reference types contain nomcp-govalues.contract.MCPToolBackendcombines caller-specific discovery and bounded execution shared with HTTP services and workers.contract.MCPResourceBackendcombines discovery and reads for browsable or URI-addressed product data.contract.MCPPromptBackendcombines discovery and rendering for client-guidance templates; rendering never executes tools.contract.MCPServerDescribersupplies product values mapped into Scoutmcp.ServerConfig.
The MCP adapter derives MCPCaller from authenticated Keel context. Tenant, actor, credential, scopes, client IP, session, transport, and trust state are never accepted as tool arguments. Remote calls fail closed without authentication; host trust is explicit and limited to a locally executed stdio composition. The adapter enforces MCPToolPolicy with Keel authorization, quota, secret, and audit infrastructure; standard annotations are client hints and never authorization rules. A long-running operation returns MCPTaskReference after durable dispatch and completes in a worker.
Scout owns mcp.BaseServer, ToolProvider, ResourceProvider, stdio/SSE/Streamable HTTP setup, envelopes, resources, text bundles, field discovery, and manifest conformance checks. Keel remains responsible for authentication middleware, authorization, quota, secrets, trusted client-IP context, and HTTP infrastructure. Scout does not define MCP HTTP DTOs in api/; MCP wire values remain in mcp-go at the protocol boundary.
Turn lifecycle
sequenceDiagram
participant Client
participant API as Conversation API
participant Reply as Reply Broker
participant Queue as Turn Dispatcher
participant Worker as Runtime Worker
participant State as Session Coordinator
participant Gateway as Model or Tool Gateway
Client->>API: Submit turn with request ID
API->>API: Authenticate, limit, reserve budget
API->>Reply: Subscribe by tenant and request
Reply-->>API: Return opaque reply route
API->>Queue: Enqueue turn and reply route
Queue-->>Worker: Lease fairly scheduled work
Worker->>State: Load durable snapshot through cache
Worker->>Gateway: Execute governed step
Gateway-->>Worker: Return guarded result or stream
Worker->>State: Commit checkpoint and idempotency result
Worker->>Reply: Publish ordered reply frame
Reply-->>API: Deliver frame
API-->>Client: Stream approved output
Worker->>Queue: Acknowledge completed turn
Subscribe before enqueueing so a fast worker cannot publish before the response route exists. Reply frames carry tenant, request, conversation, route, and sequence identity. The reply broker may retain frames briefly for reconnect, while the final result remains durable.
Starting latency budget
Treat these values as an initial measurement plan, not a universal guarantee.
| Stage | p95 target |
|---|---|
| Edge, authentication, admission, budget | 20–40 ms |
| Reply subscription and durable dispatch | 10–30 ms |
| Fair scheduling and worker lease | 20–50 ms |
| Session and graph cache reads | 5–20 ms |
| Prompt construction and optional retrieval | 50–150 ms |
| Model time to first token | 600–900 ms |
| Output guardrail and reply delivery | 20–60 ms |
| Expected first approved response | 725–1,250 ms |
Measure accepted client bytes through the first approved frame written to the socket. Record admission, queue, state load, retrieval, provider, guardrail, and reply spans separately by tenant tier, model, region, and outcome.
Create a downstream platform
1. Add Scout and keel
Use released module coordinates; never use a local replace or filesystem dependency.
go get github.com/nauticana/scout@<version>
go get github.com/nauticana/keel@v1.2.46
Import Scout contracts and keel infrastructure directly:
import (
keelport "github.com/nauticana/keel/port"
scoutcontract "github.com/nauticana/scout/contract"
)
2. Start with a layered layout
cmd/
httpsrv/ HTTP composition root
runtime/ runtime worker composition root
platform/ compilation and rollout worker
mcpsrv/ MCP composition root
internal/
common/ flags and application configuration
handler/ HTTP-only adapters and controller wiring
mcp/ MCP tool and resource providers
model/ product-specific DTOs
port/ product-specific interfaces
service/ business logic and persistence
worker/ keel worker implementations
Add only the binaries the product needs. Each cmd/<binary>/main.go parses flags, constructs dependencies, and starts one keel runtime. Composition roots contain no business logic.
3. Implement services behind interfaces
Choose one vertical slice, such as agent publication or conversation admission, and implement the smallest Scout contract set it requires.
type AgentPublishingService struct {
DB keelport.DatabaseRepository
Versions scoutcontract.AgentVersionRepository
Compiler scoutcontract.AgentCompiler
}
var _ scoutcontract.AgentPublisher = (*AgentPublishingService)(nil)
Services own repositories and query services. Use keel's named SQL pattern: constants name every query, one package-level map holds SQL, and the query service is initialized once. Use keel transactions for multi-statement atomic changes. Never add a database repository interface to Scout.
Return typed sentinel errors from services. HTTP and MCP adapters map those errors at their boundary.
4. Add HTTP handlers
Handlers parse and authenticate a request, call one service operation, and shape the response. They do not query tables, construct SQL, enforce business transitions, or start background work.
Prefer keel's handler adapters and service.HttpBackend for routing, authentication, authorization, CORS, health, and shutdown:
backend := keelservice.HttpBackend{
Journal: journal,
DB: db,
Secrets: secrets,
UserService: users,
QuotaService: quotas,
}
backend.Handle(controller.GetPublicHandlers(ctx))
backend.Handle(controller.GetAuthenticatedHandlers(ctx))
backend.Run(ctx)
Keep controller.go as wiring only. If an operation is meaningful without http.ResponseWriter, it belongs in a service. Use keel's metadata-driven REST support for ordinary table CRUD; write a custom handler only for a business operation.
For Agent Studio, construct controlplane.StudioService, then mount every route returned by handler.StudioHandler.Routes(). The handler owns no SQL or lifecycle rule. Products retaining a legacy display-credit scale set the handler's model and test-result mappers. Scout seeds the AGENT_STUDIO verbs and the AGENT_ADMIN and AGENT_OPER roles; the application assigns those roles to users.
5. Add workers
Embed worker.AbstractWorker and implement either keel's worker.QueueWorker for a standard claim/process loop or worker.JobWorker for a custom periodic pass. Use worker.LeasedQueueWorker when stale workers must be unable to commit after lease expiry.
w := &appworker.RuntimeWorker{
AbstractWorker: keelworker.AbstractWorker{
Caption: "runtime",
Interval: 1,
HCPort: 8101,
LoadConfig: common.LoadConfig,
},
}
if err := w.Run(context.Background(), w); err != nil {
log.Fatal(err)
}
A queue worker supplies named pending, claim, and reclaim queries plus one-job logic. Its service calls still own business state changes. Add a compile-time assertion for every worker contract.
For runtime turns, preserve this order:
- Claim with a lease token.
- Load the pinned graph and newest durable snapshot.
- Acquire tenant concurrency and execution permits.
- Begin the step idempotency record.
- Execute through governed model, knowledge, or tool interfaces.
- Commit the idempotency result and durable checkpoint atomically where possible.
- Refresh cache and publish approved reply frames.
- Acknowledge the queue message.
6. Add an MCP server
Implement the applicable Scout MCP catalogs and operations on a product service. Keep transport decoding out of that service so HTTP, MCP, workers, and tests can call the same behavior. A thin product provider maps Scout definitions and results to Scout's SDK-facing MCP types.
The composition root constructs github.com/nauticana/scout/mcp.BaseServer from MCPServerDescriber, wraps product operations as Scout mcp.ToolProvider and mcp.ResourceProvider values, and registers them:
srv := scoutmcp.NewServer(scoutmcp.ServerConfig{
Name: "example-agents",
Version: version,
Instructions: instructions,
Source: "example",
})
srv.Register(searchTool, publishTool)
srv.RegisterResource(agentCatalogResource)
The adapter performs only boundary work:
- Convert Keel request context into
domain.MCPCaller; never read tenant identity from arguments. - Map JSON schemas and annotations from
MCPToolDefinitioninto the Scout provider definition. - Enforce required scopes, quota, approval, audit, credential, and egress policy before delegation.
- Call one
MCPToolExecutor,MCPResourceReader, orMCPPromptRenderermethod. - Project
MCPToolResultthrough Scout envelopes and attach evidence resource links or a durable task reference. - Map typed domain errors to protocol errors at the adapter boundary.
Tool annotations describe expected behavior to clients but do not grant access. Keep read and write scopes distinct, fail closed when caller context or required provenance is unavailable, and dispatch long-running work to a worker instead of holding an MCP request open.
Select transport in the binary:
stdiois appropriate for a locally executed client under host trust.- Streamable HTTP or SSE requires authentication, quota middleware, trusted proxy configuration, and a public health endpoint.
- OAuth-protected MCP routes should use keel's OAuth resource middleware.
Run mcp/mcptest manifest and tool-text conformance checks before publishing a server.
Database schema
Scout's schema is the relational source of truth. Do not maintain handwritten DDL beside it.
The schema uses portable types supported by keel's PostgreSQL and MySQL dialects. Structured definitions are canonical JSON stored as TEXT and validated at service or compilation boundaries. Timestamps use TIMESTAMP; large data stays outside the relational database behind URI and digest columns.
Every deployment installs keel tenant_management because agent_tenant is a child of keel's business_partner. Keel declares tenant_management as depending on both core and geo, so all three groups must be generated and installed in dependency order: core, geo, tenant_management, then Scout. Scout does not copy or fork those infrastructure tables. See DATABASE.md for the dependency boundary and Scout-only ER diagrams.
Schema groups
| Group | Tables | Purpose |
|---|---|---|
catalog |
7 | Currency, priority, lifecycle, and usage catalogs |
tenancy |
4 | Tenant identity, active policies, and quotas |
control_plane |
28 | Studio drafts, prompts, lifecycle audit, agents, tools, compiled graphs, knowledge, models, pricing |
runtime |
7 | Conversations, turns, checkpoints, replay, budgets, usage |
release |
8 | Platform artifacts, rings, compatibility results, audit |
schema/dependency.yml orders Scout-owned groups. Keel's own dependency manifest orders its groups, while the generator input below selects the required Keel groups explicitly. Each Scout group has an ab_meta.yml that lists its tables in dependency order. Every table lives in its own <table>.yml file.
Surrogate-ID tables declare explicit sequence metadata and rely on keel's core table_sequence_usage registry. Clients never choose surrogate IDs.
Generate dialect-specific DDL
The Go tool declaration pins keel's compiler. Resolve the matching keel schema through its module coordinate and generate one ordered build artifact:
keel_schema_dir="$(go list -m -f '{{.Dir}}' github.com/nauticana/keel)/schema"
go tool schemagen -dialect pgsql -input "${keel_schema_dir}/core,${keel_schema_dir}/geo,${keel_schema_dir}/tenant_management,schema" -seed schema/seed -out build/scout_pgsql.sql
go tool schemagen -dialect mysql -input "${keel_schema_dir}/core,${keel_schema_dir}/geo,${keel_schema_dir}/tenant_management,schema" -out build/scout_mysql.sql
The combined schema currently contains 38 selected keel tables and 54 Scout tables. The explicit input order respects keel's declaration that tenant_management depends on core and geo, then adds Scout. Run both commands in CI. The compiler validates table order, foreign-key targets, primary keys, sequences, indexes, and duplicate constraint names before producing DDL. Never commit a dialect-specific SQL file as another schema source.
schema/seed/control_plane.yml seeds execution kinds, supported language codes, Studio authorization verbs, Agent Studio roles, page/table permissions, REST headers, and foreign-key lookup metadata. schema/seed/tenancy.yml seeds capacity_class with shared and dedicated. Each constant domain maps its consuming column through constant_lookup. The seed YAML is authoritative. The pinned keel seed emitter currently produces PostgreSQL ON CONFLICT syntax, so the MySQL command validates DDL without appending seed DML; revisit seed emission if MySQL becomes a deployment target.
Persistence rules
- Use keel
DatabaseRepository,QueryService,TxQueryService, and table services. - Keep named SQL in services, not handlers or workers.
- Treat
TableService.Updateas a full-row update; use read-modify-write or targeted named SQL. - Use composite foreign keys to preserve tenant ownership through every relation.
- Treat every foreign-key constraint name as a public keel REST relation name: use the child collection name for its owning parent and a role-qualified collection name for additional parents.
- Never rename a foreign key casually; update
foreign_key_lookup,rest_api_child, generated clients, and compatibility contracts together. - Store accounting values in minor units and derive display precision from
currency.exponent. - Use object storage for definition payloads, conversation content, checkpoints, and audit payloads that can grow without bound.
- Treat vector storage, caches, queues, and reply brokers as external provider concerns behind Scout contracts.
Configuration and providers
Every deployable setting is a Go flag declared in the downstream internal/common/variables.go. Load application configuration through keel after the database is available. Do not read environment variables directly.
Secrets contain only secret material and come from keel's configured secret provider. Schema rows store secret references, never secret values or credentials embedded in URLs.
For each pluggable concern:
- Depend on a Scout or downstream interface.
- Put each provider in its own
internal/service/<concern>_<provider>.gofile. - Select it in a composition factory using
--<concern>_mode. - Fail startup when the selected provider lacks required configuration.
- Add
var _ Interface = (*Provider)(nil).
Typical provider concerns include model inference, embeddings, vector search, durable dispatch, reply delivery, hot session cache, object storage, and tool transport.
Reliability and isolation
Durable dispatch and fairness
Use a fixed partition pool rather than one physical queue per tenant. Deterministically shuffle-shard tenants over a small partition subset, preserve conversation ordering, and schedule bounded tenant-local ready queues by weight and concurrency availability.
The queue supplies durability and backpressure. Fairness belongs in FairTurnScheduler, not in an assumption about broker behavior. Dedicated tenants may receive isolated partitions and worker pools without changing the contract.
Checkpoint and replay
Persist each completed step before beginning the next. A replacement worker loads the latest revision and uses StepIdempotencyStore.Begin to reuse a committed result or replay only unfinished work. External tools receive the same stable idempotency key when supported.
Write the durable checkpoint before refreshing the hot cache. Reject optimistic revision conflicts and invalidate stale cache entries. Never acknowledge work from cache state alone.
Guardrails
Apply input policy before model submission, tool policy before arguments leave the platform, tool-output policy before results re-enter the graph, and output policy before each reply frame is published. Policies that require wider context may buffer output at an explicit latency cost.
Tenant controls
Admission limits, token and cost reservations, weighted scheduling, concurrency leases, provider quotas, bounded queues, and cost circuit breakers must all use tenant identity. Regulated or high-volume tenants can opt into dedicated capacity through policy rather than provider-specific branches in business services.
Knowledge and tools
Knowledge is versioned independently because it requires ingestion, immutable document bindings, embeddings, tenant-filtered vector search, and source attribution. Revalidate relational document bindings after vector search before loading content.
The model never calls a destination directly. A governed tool path resolves the registered immutable version, authorizes tenant and agent access, retrieves scoped credentials, validates egress, applies timeout/retry/circuit-breaker policy, validates output, and records a redacted audit event.
Release safety
Agent rollout and platform rollout solve different problems:
AgentVersionTrafficManagerroutes one tenant's conversations between immutable agent versions.PlatformReleaseRolloutControlleradvances a platform artifact through tenant rings.
Before advancing a platform ring, run a risk-stratified corpus through AgentContractTestRunner and evaluate latency, errors, cost, quality, and compatibility. Roll back platform code without changing tenant agent versions.
Testing strategy
| Layer | Required coverage |
|---|---|
| Domain and services | Pure unit tests without HTTP scaffolding |
| Persistence | Named-query argument order, transaction rollback, tenant scoping, FK behavior |
| Handlers | Authentication, request decoding, one service call, sentinel error mapping |
| Workers | Claim/reclaim, lease loss, retry, idempotency, crash after each durable boundary |
| MCP | Manifest parity, schemas, annotations, caller derivation, scope and quota enforcement, envelopes, provenance, prompt purity |
| Schema | Parse and generate both PostgreSQL and MySQL DDL |
| Runtime | Duplicate delivery, stale cache, provider timeout, partial stream, reconnect |
| Scale | Queue fairness, tenant isolation, first-approved-response latency, cost ceilings |
Use fakes for Scout contracts and keel ports. Test service logic directly; an HTTP test should not be the only proof of a business rule.
Recommended implementation order
- Pin Scout and keel versions and generate both schema dialects.
- Implement Studio draft reads, prompt resolution, compilation, and publication.
- Add Agent Studio handlers through keel
HttpBackend. - Implement tenant policy, graph persistence, and deployment resolution.
- Implement durable turn records, idempotency, and session coordination.
- Add a queue-backed runtime worker with one deterministic step kind.
- Add model routing and one provider adapter.
- Add guarded reply streaming and reconnect behavior.
- Add tool governance and one transport adapter.
- Add knowledge ingestion and retrieval if the product needs it.
- Add MCP providers backed by the same services.
- Add compatibility runs, rollout rings, load tests, and failure injection.
This order proves durable execution before expanding provider breadth.
Contribution checklist
- The change adds only shared agent-domain contracts, DTOs, reusable services, schema, or documentation.
- No keel infrastructure primitive is duplicated.
- Public interfaces use provider-neutral domain types.
- Comments are one concise line per exported type or method.
- Tenant identity is present at every storage and provider boundary.
- Money uses minor units and an explicit currency.
- New surrogate tables declare explicit sequences and foreign keys.
- Schema generation succeeds for PostgreSQL and MySQL.
- Handlers remain HTTP-only and workers delegate business logic to services.
- New provider implementations stay downstream and include interface assertions.
README.mdremains the single developer guide; API and database files remain references only.
License
Copyright 2026 Mustafa Karli and Scout contributors.
Licensed under the Apache License, Version 2.0. See LICENSE.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package mcp provides Scout MCP transports, registration, envelopes, and discovery helpers.
|
Package mcp provides Scout MCP transports, registration, envelopes, and discovery helpers. |
|
service
|
|