Eino + Agent Guild preflight
An optional Go integration that checks a disclosed MCP endpoint immediately before its native tool call. It owns the official MCP SDK client and installs the guard on that exact session. Eino aliases, same-name collisions and WithToolList reuse of these tools therefore keep the selected endpoint's policy.
This is an independent AgentTanuki project, licensed under Apache-2.0. It does not modify Eino or install a global hook.
Scope and consent
Connect requires an explicit endpoint, policy and DiscloseEndpointAndAllowProbing: true. It connects to the MCP endpoint and discovers its tools. Before each subsequent tools/call, it sends only the full endpoint URL to GET https://agent-guild-5d5r.onrender.com/preflight?url=.... Guild actively probes that endpoint, including its public discovery surface. The guard sends no prompts, tool arguments, credentials or payment data to Guild. Raw tool names are available only in the optional local audit callback.
Use only an endpoint you are authorized to disclose and permit Guild to probe. Production targets must be public HTTPS URLs, without user information, query strings or fragments. Never provide credentials hidden in the URL path: opaque path credentials cannot be recognized by URL syntax alone. Private addresses and invalid configurations are rejected before connection/disclosure. No OAuth, API keys, caller transports or proxy settings are accepted.
The free response is an unsigned, bounded observation from Guild's network vantage. This application's MCP connection has a separate network vantage and pins its own screened DNS result. They can reach different machines behind DNS/CDNs, and the endpoint may change behavior between observation and execution. Exact local session binding does not prove remote ownership or identity. Card retrieval, signature presence, or a payment challenge does not prove signature validity, protocol conformance, safety, successful tasks or paid execution. This integration never calls /check, /preflight/deep, registration, passport verification, billing or payment endpoints.
Supported runtime
The module pins Eino v0.9.19, its officialmcp adapter v0.1.1, and the official MCP Go SDK v1.6.1. The adapter and SDK require Go 1.25 or later. Local validation used Go 1.26.8 on macOS arm64; CI is configured for Go 1.26.8 and 1.27.1 on Linux. A configured CI matrix is not a claim that an unpublished run has passed.
Build the source with:
go mod download
go test -race -count=1 ./...
go vet ./...
go build ./...
go mod verify
Native use
Once the release is available, install the pinned version:
go get github.com/AgentTanuki/eino-agentguild@v0.1.0
Before release availability, evaluate this source using a local Go module replacement. The import path is github.com/AgentTanuki/eino-agentguild; use a reviewed published release or commit for deployed applications.
policy := agentguild.StrictPolicy()
// Explicit operator decision: these two missing observations are tolerable
// for this application. They remain unknown in the retained evidence.
policy.AllowedUnknown = []string{"payment_claim_holds", "independent_evidence"}
connection, err := agentguild.Connect(ctx, agentguild.Config{
EndpointURL: operatorApprovedEndpoint,
DiscloseEndpointAndAllowProbing: true,
Policy: policy,
OnDecision: func(d agentguild.Decision) {
auditLocalDecision(d) // concurrency-safe; must return promptly
},
})
if err != nil { return err }
defer connection.Close()
toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
Tools: connection.Tools(),
})
Use the resulting native ToolsNode in your existing Eino graph/agent, or invoke it with an operator-selected tool call. The operator example compiles this API and makes zero calls without explicit flags. It performs discovery only unless a tool is named. It uses strict policy plus any explicitly listed tolerated unknowns; applications needing other policies should configure the library directly.
StrictPolicy() accepts no_failed_checks, requires endpoint_reachable and protocol_handshake to be proven, and allows no failed or unknown checks. Many ordinary MCP endpoints lack an A2A card or independent evidence, so this default intentionally denies them. For a justified application policy, select AcceptedVerdicts, RequiredProven, AllowedUnknown and AllowedFailed explicitly. For example, a caller may omit protocol_handshake from RequiredProven and list it in AllowedUnknown when a bounded unauthenticated probe cannot establish the handshake. This preserves an unknown as unknown. do_not_delegate is always denied, irrespective of other settings. Required-proven checks take precedence over tolerance lists; no configuration automatically expands itself.
Unavailable, timed-out, malformed, conflicting, stale-indicated or wrong-target responses deny the call. The parser requires all six current contract checks, unique names, valid statuses, exact failed/unknown/scored summaries and a consistent verdict. Duplicate JSON keys, excessive nesting, missing fields and oversized bodies are rejected. New cache-result fields require review. Additional check names are preserved and evaluated by the same policy rules.
Evidence and network behavior
OnDecision receives the actual service origin and target, local request/receipt times, raw bounded JSON, parsed verdict/checks/failed/unknowns, response cache headers and a fixture marker. Remote headline, detail, method, limits and extension data remain untrusted evidence. They are never interpreted as instructions or injected into model messages by this package. Policy errors expose fixed reason codes, not transport URLs or remote prose. Changing a callback report cannot change the already computed decision; the callback is synchronous and should not block.
Each tool attempt gets a fresh preflight GET. There is no client cache; requests ask for no caching, and positive/invalid Age headers are rejected. This does not authenticate a remote freshness claim. Preflight defaults to an 8-second total deadline (configurable up to 15 seconds), including DNS, connection, headers and a maximum 64 KiB body. It follows no redirects and uses no environment proxy. Its HTTP/1 transport disables connection reuse to prevent Go's reused-connection retry behavior.
The owned MCP transport also disables HTTP connection reuse, redirects and proxies. It screens every resolved address and pins the connection to one allowed address; the URL hostname remains the TLS verification name. Setup/discovery has a 10-second total deadline and each MCP HTTP request a 5-second deadline. The native session has no OAuth, automatic session reconnection, SDK retry or standalone SSE. HTTP socket reuse is separate from the logical MCP session. A failed session requires a deliberate new Connect, with the same explicit endpoint/policy requirements. Outer application retries get a fresh preflight; the guard cannot undo work already dispatched.
Boundary limitations
- Initialization, notifications and
tools/list discovery happen before preflight; initial server metadata remains untrusted. This guard covers subsequent tools/call, not discovery, resources, prompts, A2A, arbitrary application network calls or direct clients created elsewhere.
- Only tools returned by this factory are guarded.
WithToolList can intentionally replace them with arbitrary unguarded tools; those tools are outside coverage. Local tools remain usable under native Eino behavior. Eino's duplicate-name selection is last-wins; use ToolNamePrefix for clarity, but enforcement never trusts the exposed name as proof of origin.
Invoke, Eino's native invokable-to-stream conversion, parallel calls and outer middleware retries retain the actual session guard. Eino does not automatically cancel sibling tools on another tool's denial. Existing earlier caller denial prevents the observation. Outer code can hide returned errors; it cannot make this denied native call dispatch.
- Native
officialmcp result formatting and error conversion remain unchanged. The guard does not validate or sanitize tool content. Metadata mutation does not move the session or raw wire tool name; do not mutate tool metadata concurrently with agent execution.
Close is idempotent and cancels pending preflight before closing the native session. Stop agent runs before closing. Cancellation cannot reverse completed or already dispatched work.
Loopback demo
go run -tags agentguild_fixture ./cmd/offline-demo
The demo starts an official SDK MCP server and a synthetic preflight HTTP fixture on exact 127.0.0.1 origins. It uses the real native Eino ToolsNode, allows one call, then denies another and asserts the protected tool ran only once. It uses no model/provider, public Guild request, account or payment. Unknowns remain visible. ConnectLoopbackFixture exists only with the explicit agentguild_fixture build tag and rejects empty or non-loopback origins. Do not use that build tag for production binaries.
Source contract
These tests demonstrate synthetic native integration behavior. They are not evidence of external agents, adoption, passports, revenue, paid tasks or public endpoint compatibility.