Remote MCP connections
Use this reference to understand the Environment-owned source contract for remote MCP (Model Context Protocol) connections. A connection stores an endpoint and its approved configuration; it does not by itself authorize or perform a remote tool call. The Environment source candidate is unpublished and not deployed.
Source boundary
Agentweaver.Environment owns remote MCP connection configuration and catalog pin records. This source candidate is not published or deployed.
The Environment API stores connection state in PostgreSQL. Each create or update writes an immutable configuration revision. The connection head points to the current revision. Row revisions support compare-and-swap. Each mutation also requires an idempotency key.
The initial migration adds four remote MCP tables to the existing Environment schema without recreating existing tables. A later EF migration adds the typed transport-adapter pin to immutable configuration rows. It backfills existing rows with the current Streamable HTTP option snapshot and leaves their configuration digests unchanged. The explicit Environment migration path applies pending migrations under its existing lock, and regular startup rejects a database with pending migrations. PostgreSQL tests check both the model snapshot and the existing-row backfill.
Read routes require current ReadProjects authority. Write routes require current WriteProjects authority. Environment reads current authorization context from Projects & Config and repeats authority checks around mutations.
Connection mutation bodies do not accept OAuth codes, access tokens, or client secrets. API calls use the current bearer in the Authorization header; Environment forwards it to Projects & Config for authorization and never persists it.
Routes
| Method and path | Contract |
|---|---|
GET /api/projects/{projectId}/remote-mcp/connections | List live connections with cursor pages of up to 100 records. |
POST /api/projects/{projectId}/remote-mcp/connections | Create a Draft connection with an idempotency key. |
GET /api/projects/{projectId}/remote-mcp/connections/{connectionId} | Read the current head, immutable configuration, and current catalog pin. |
GET /api/projects/{projectId}/remote-mcp/connections/{connectionId}/configurations/{configurationRevision} | Read one exact immutable configuration revision. |
PUT /api/projects/{projectId}/remote-mcp/connections/{connectionId}/identity-binding | CAS an Identity UUID-N reference into a new immutable configuration revision. |
PUT /api/projects/{projectId}/remote-mcp/connections/{connectionId} | Write a new configuration revision and return the connection to Draft. |
POST /api/projects/{projectId}/remote-mcp/connections/{connectionId}/enable | Enable a connection against the expected row revision. |
POST /api/projects/{projectId}/remote-mcp/connections/{connectionId}/disable | Disable a connection and clear its current catalog pointer. |
DELETE /api/projects/{projectId}/remote-mcp/connections/{connectionId} | Mark a connection Removed without erasing its history. Send a JSON body with expectedRowRevision and idempotencyKey. |
POST /api/projects/{projectId}/remote-mcp/connections/{connectionId}/discover | Discover the tool catalog of an enabled connection and pin it as a new immutable discovery revision. Send expectedRowRevision and idempotencyKey. |
POST /api/projects/{projectId}/remote-mcp/connections/{connectionId}/invocation-validity | Compare a review reference and one tool revision with the current head. Returns RemoteMcpInvocationValidity. |
GET /api/projects/{projectId}/remote-mcp/directory | List the project connections with configuration revisions, configuration checksums, and catalog state. Query: pageSize (1–100, default 50) and after. |
GET /api/projects/{projectId}/remote-mcp/registry/servers | Read one page of the configured MCP Registry. Query: cursor. |
The Gateway exposes the directory route as GET /api/v1/projects/{projectId}/remote-mcp/connections (operation listRemoteMcpConnections). The other routes stay on the Environment owner.
Mutation bodies include an idempotency key. Create and update accept transportProfile, which defaults to the only supported streamableHttp20250618 value; another profile fails before persistence. Update and state mutations also include the expected row revision. The DELETE request uses RemoteMcpConnectionStateRequest as a JSON body; it does not move these values to headers or query parameters. The Identity-binding request uses operationId as its durable idempotency key and includes the expected configuration revision and SHA-256. A successful bind returns RemoteMcpIdentityBindingReceipt with projectId, connectionId, operationId, finalConfigurationRevision, finalConfigurationSha256, and identityBindingReference. Success and replay return 200; malformed references return 400; current authorization failures return 401 or 403; missing connections return 404; stale pins, mode conflicts, or idempotency conflicts return 409. Connection handlers set Cache-Control: no-store on responses that they handle.
Configuration and identity
For Network Policy provider selection and runtime settings, see the planned provider configuration guide and runtime configuration reference.
RemoteMcpConnectionConfiguration contains the project and connection IDs, configuration revision, display name, canonical endpoint and optional resource URI, authentication mode, transport profile, a typed transport-adapter options snapshot, optional exact Registry pin, optional Identity correlation handle, and ConfigurationSha256.
Environment supports only the StreamableHttp20250618 transport profile and fails closed for another value. Its credential-free adapter pin records the adapter ID and version, options schema and revision, protocol version, request timeout, response-size limit, tools/list page limit, and an options SHA-256. The option values are currently fixed. The profile is already included in ConfigurationSha256, so the deterministic profile-to-options mapping preserves existing configuration digests. Endpoints and resource URIs must be absolute HTTPS DNS URIs without user information or fragments.
HTTP responses use camel-case enum values: draft, enabled, disabled, and removed. Authentication is none or delegatedOAuth; the transport is streamableHttp20250618. An Identity reference must be a nonempty canonical lower-case UUID-N value.
Delegated OAuth requires an explicit resource URI. A connection can remain unlinked while the Identity owner has not issued a correlation handle. In this state, IdentityBindingReference is null.
The Identity correlation handle is an opaque lower-case UUID-N generated by Identity. It does not grant consent, access, or credential use. The identity-binding route requires a current caller with WriteProjects authority, checks the expected current configuration revision and hash, and writes a new immutable revision containing the handle. It returns the final revision and hash for Identity to pin. A bind returns the connection to Draft and clears the current catalog pointer. A later connection configuration change also creates a new revision and clears the handle, so Identity must re-read the current head and exact configuration before it creates pending consent.
The operation ID is scoped to the authorized actor in the durable request fingerprint. Repeating the same operation for that actor returns the original receipt. A different request or actor that reuses the operation ID conflicts. Every replay still requires a currently valid bearer with the Environment audience and fresh Projects WriteProjects authority. Environment does not accept or store OAuth codes, access tokens, client secrets, or bearer tokens. This route does not establish a background or service-to-service actor for OAuth callback or refresh.
ConfigurationSha256 binds the immutable Environment configuration revision. It includes the endpoint, resource, authentication mode, Identity handle, transport profile, and Registry pin. The separate adapter-options digest binds the exact typed options snapshot; it does not replace or change the connection digest. Neither digest is the Identity binding hash.
Registry metadata and tool schemas
RemoteMcpMetadataParser is a pure parser. It does not perform network requests. It accepts bounded JSON documents, rejects duplicate object properties, and enforces depth, item, cursor, tool, and schema byte limits.
Limits are 1 MiB per document, depth 32, 100 Registry page items, 2,048 cursor characters, 128 tools, and 256 KiB per schema.
The Registry parser requires an exact server version. It rejects latest, templated endpoints, unsupported transports, and Registry-supplied headers. It accepts only static HTTPS DNS endpoints for streamable-http.
The tools-list parser accepts at most 128 tools across all pages. Each tool must have a unique name and an object input schema. It checks common JSON Schema structure in nested definitions, including schema maps, legacy dependencies, string-name arrays, non-empty composition and tuple-schema arrays, and numeric constraints. Mathematically integral decimal and exponent forms are accepted for count constraints. Boolean schemas are allowed below the root. The parser bounds schema depth and bytes, rejects duplicate JSON properties, and never fetches a $ref. BuildCatalog joins the pages and rejects a duplicate name on any page. The parser returns exact schema, tool, and catalog digests.
Discovery
RemoteMcpDiscoveryService runs discovery for one enabled connection. It requires current WriteProjects authority and the expected row revision. It follows tools/list cursors until the last page and enforces the pinned adapter's protocol version, response-size, and page limits as well as the parser limits. When the connection has a Registry pin, it reads the exact Registry version again and compares the digest with the pin. A changed digest fails discovery with 409 and keeps the old catalog.
Discovery calls IRemoteMcpDiscoverySource. The default UnavailableRemoteMcpDiscoverySource returns 503 remote_mcp_discovery_transport_unavailable and sends no network request. A trusted transport source replaces it when one is registered. Discovery output is a candidate for review. It does not accept a tool for use.
Trusted L7 mediation and enforced network policy are deferred. The default source remains unavailable until a trusted transport is registered. Connection and catalog records do not prove live discovery or authorize a remote tool call.
A repeated discovery request with the same idempotency key returns the first result. Discovery reuses the existing SaveDiscovery idempotency operation, so it needs no new migration or grant.
Pin and execution limits
Configuration revisions and saved catalog rows are immutable. Disable, removal, or a configuration change clears the current catalog pointer. The old rows remain available as history. An unavailable discovery source fails the request and does not change a saved pin.
Review references and invocation validity
Agentweaver.Abstractions defines the contracts that reviewed tool snapshots store and check:
RemoteMcpConnectionReviewReferencepins the project, connection, configuration revision, configuration SHA-256, discovery revision, and catalog SHA-256. Any change to the endpoint, resource, transport, authentication mode, Identity binding, or Registry pin creates a new configuration checksum. A token rotation does not change the reference.RemoteMcpConnectionReview.Evaluatecompares a reference and one tool revision with the current snapshot. It returnsRemoteMcpInvocationValiditywith aRemoteMcpInvocationDenialreason.- A changed configuration, catalog, or tool returns
reviewRequired: true. The caller needs a new review. Environment never accepts a changed connection on its own. - A delegated OAuth connection returns
credentialValidityRequired: trueand the Identity binding reference. The caller must also get a current Identity credential decision before it sends a request. RemoteMcpConnectionDirectoryEntryandRemoteMcpConnectionDirectoryPagecarry the safe directory fields. They never contain credentials, tokens, or tool schemas.
Environment rejects run-bound callers on these routes. These routes don't send requests to the remote server. The trusted transport admits and sends run-time calls.
AgentSuite tool providers
An AgentSuite package describes a remote Streamable HTTP provider with a ToolProvider document listed in its ToolProviderCatalog. The package declares the tools that the agent needs. It never contains the endpoint, a secret, or a credential. A project binds each provider (id@version) to one of its Remote MCP connections by connection ID. The connection ID is a GUID in D format.
RemoteMcpToolProviderRequirement carries the provider reference, the streamable-http transport, whether the provider declares static header secrets, and each tool's name, effect, approval mode, and delegated authorization flag. RemoteMcpToolProviderBinding.Evaluate checks the requirement against the current connection snapshot and returns RemoteMcpToolProviderBindingResult:
- A bound result pins the configuration revision and SHA-256, the discovery revision, the catalog SHA-256, and the exact revision of each required tool. Reviewed tool snapshots store these pins.
- A denied result gives one reason:
ConnectionNotEnabled,CatalogMissing,CatalogStale,ToolMissing(with the missing tool names),DelegatedAuthorizationUnavailable, orHeaderSecretsUnsupported.
The connection configuration replaces the provider's endpoint references:
| Provider field | Source of truth |
|---|---|
endpointRef | Connection endpoint URI and OAuth resource URI |
network[].destinationRef | Egress derived from the connection endpoint by the trusted transport |
delegatedAuthorization.credential.bindingRef | Connection Identity binding. Requires delegated OAuth. |
headers[].secretRef | Not supported. Binding returns HeaderSecretsUnsupported. |
timeouts | Capped by Environment execution limits |
Binding never reads a credential. Run-time use still requires RemoteMcpConnectionReview.Evaluate and a current Identity credential decision.
Import records
The AgentSuite import stores each binding as toolConnections[{ toolProvider, connectionId }], where toolProvider is id@version. Projects & Config checks only the shape: connectionId must be a lowercase D format GUID and not the empty GUID. It never calls Environment and never resolves an endpoint or secret. Import reports these blockers:
tool_connection_unbound: a remote provider has no connection ID.tool_connection_header_secrets_unsupported: the provider declares headersecretRefvalues. This matchesHeaderSecretsUnsupported.
When a reviewed tool snapshot is built, the caller derives RemoteMcpToolProviderRequirement from the import record and calls RemoteMcpToolProviderBinding.Evaluate with the bound connection's current snapshot. Import doesn't call Environment, so a connection can change between import and review. The binding check at review time catches that change.
Credential validity
Identity owns delegated OAuth credentials. POST /internal/connections/remote-mcp/{connectionId}/validity (Gateway: POST /api/connections/remote-mcp/v1/{connectionId}/validity) answers whether the caller's credential is valid now for an expected configuration revision and checksum. It returns RemoteMcpCredentialValidity:
validand aRemoteMcpCredentialDenialreason:NotConnected,ConsentPending,RefreshPending,RefreshIndeterminate,Disconnected,Revoked,Expired,ConfigurationChanged,ConnectionRemoved, orCredentialChanged.- The connection, credential, and configuration revisions, the configuration SHA-256, and the access token expiry for a valid credential.
The response never contains token values, secret identifiers, or secret versions. Identity checks project read authority before and after the Environment read, then reads the connection row again. A refresh, rotation, or disconnect during those waits returns CredentialChanged. The route never refreshes or recovers a credential.
RemoteMcpRemoteUse.Admit combines Environment invocation validity with Identity credential validity. It allows a call only when Environment allows the reviewed connection and, for delegated OAuth, Identity reports a valid credential at the same configuration revision and checksum. A disconnected or revoked credential blocks use even when the Environment connection stays enabled. Any component that sends a request to a remote MCP server, such as the runtime that runs reviewed tool snapshots or the network path that carries the request, calls Admit immediately before each send. A decision from an earlier call is never reused, because a credential can be revoked or a configuration can change between calls.
Disconnect clears Identity's local token references. It does not call a provider revocation endpoint.
The Cilium Environment provider enforces L3/L4 egress only. Remote MCP needs L7 mediation, so this provider does not authorize or execute MCP requests.
Separate optional TLS profiles register Environment's fixed Identity and Orchestrator owner clients. They require explicit existing service identities, certificate files, peer pins, and bounded timeouts; an ordinary bearer cannot select these service roles. Their HTTP handlers disable redirects and cookies. The matching private listeners reject another service or port before reading a proof. These registrations do not supply current applied L7 evidence or enable a send.
Environment's reviewed-tool recipient is separately disabled by default. Environment:RemoteMcpCallVerification:EnableReviewedToolRecipient maps its Kestrel:Endpoints:RemoteMcpReviewedToolTransport:Url listener. It reuses the configured Orchestrator service identity and certificate pins. Its port must match that profile's ListenerPort and differ from every other listener. It cannot share the Identity verification listener or accept the Identity peer. RequireOrchestratorPeer rejects missing configuration, ordinary HTTPS, the wrong port, or another service before the receiving route reads the proof. The receiving route also requires the configured current-call verifier. Startup rejects an enabled recipient without that verifier or its registered services. With both options, Program maps the private dispatch route. This configuration does not establish network enforcement, issue credentials, or enable remote sends. Public domains and SSL remain owned by AKS Application Routing. Public managed HTTPS and forwarded headers do not replace the private client-certificate check.
Internal reviewed-call contracts
RemoteMcpCurrentCallVerificationRequest carries the full RemoteToolResultProof and a matching tools/call binding. It has no caller-defined wire digest or RPC identity. The native handler must read and check the actual bounded request bytes. The reviewed NativeCallId remains separate from the transport's RPC ID.
RemoteMcpCurrentCallContract.ValidateToolCallBytes checks an actual request against that existing proof and binding. It requires the exact HTTP method, target, tools/call operation, tool name, and canonical arguments digest. The body limit is 1 MiB; canonical arguments retain the existing 256-KiB limit. It rejects batches, notifications, responses, malformed JSON, duplicate keys at any depth, and unsupported request fields. Object ordering, whitespace, and equivalent string escapes do not change the argument match. Array ordering and numeric spelling retain the existing reviewed digest rules. Omitted arguments match only the reviewed empty object. A string or number JSON-RPC request ID remains independent of NativeCallId. Optional MCP metadata and progress tokens retain their protocol types. Metadata is not actor or approval authority.
The Orchestrator caller checks its private copy with this helper before its late authority check and transport handoff. It supplies the reviewed HTTP method and target, not an actual emitted request. The receiving native transport must still check its built upstream method, target, and exact body bytes before a send. The Environment receiver now uses this helper on its built upstream request. The helper itself does not authenticate a caller, verify current owner state, prove network enforcement, return an acknowledgment, or permit credential use. Those separate checks remain required before any protected effect.
The Orchestrator's IReviewedRemoteToolTransport.SendAsync receives the original HttpContext, full owner-built proof, selected snapshot, and copied request bytes. The caller repeats current authority checks after serialization and before this handoff. The receiving host must authenticate that context and repeat owner and network checks. The proof and MCP request metadata do not establish actor or network authority. A transport denial returns no result and does not trigger a fallback or retry. The optional owner adapter is disabled by default. Orchestrator:RemoteMcpCallVerification:EnableReviewedToolTransport registers it. Startup requires the runtime registration owner and the existing Environment TLS profile. Its PeerOwnerAddress must identify Environment's private reviewed-tool listener, not a public managed route or the Identity verification listener. The adapter sends only to this fixed owner, with no redirects, cookies, retry, or fallback. It forwards the authenticated actor's bearer and owner-built tenant binding. It repeats actor and expiry checks after the wait. The private result must match the entire reviewed proof; only ContentClass can change. The adapter does not register or enable the native SDK hook.
RemoteMcpNativeCallRequest carries the full current-call request and a byte array. POST /internal/remote-mcp/current-call/dispatch accepts only the dedicated Orchestrator peer. This check precedes body reads and verifier creation. The route limits the JSON envelope to 2 MiB, including unknown-length bodies. The copied MCP request and private result each have a 1-MiB limit. The route builds a credential-free POST to the exact reviewed target. It checks that method, target, and the same copied bytes before current owner verification. The result contract binds every proof field except the actual content class.
The receiver has no sender. Missing forced network enforcement still returns 503 with remote_mcp_forced_network_enforcement_unavailable. Even a successful verification cannot enable a send: the receiver then returns 503 with remote_mcp_native_transport_unavailable. An unavailable recipient returns 503; a wrong peer returns 403. Invalid content type returns 415, an oversized envelope returns 413, and a malformed request returns 400. These handled responses use Cache-Control: no-store. The caller uses the shared owner HTTP errors for denied or unavailable responses. No denial produces a result, retry, credential read, or remote send.
Identity's RemoteMcpCurrentCallVerificationClient.VerifyAsync authenticates the original actor with the Broker validation scheme. It forwards that actor's bearer to the fixed Environment owner over the separate service TLS profile. The acknowledgment binds the complete proof, call, snapshot, and current policy. Identity must repeat this read after a credential wait and reject changed authority. This acknowledgment alone does not permit credential delivery.
Environment's current-call verifier is disabled by default. Environment:RemoteMcpCallVerification:EnableCurrentCallVerifier maps POST /internal/remote-mcp/current-call/verify only when explicitly enabled. Startup then requires both existing trusted owner TLS profiles and the validated Environment:NetworkPolicy:RemoteMcpManagedGateway options.
| Option | Required value |
|---|---|
ProviderId | Exact selected managed gateway provider ID. |
AdapterVersion | Exact selected adapter version. |
OptionsSchemaVersion | 1. |
OptionsRevision | Exact selected options revision. |
GatewayEndpoint | Canonical HTTPS gateway endpoint. |
The verifier uses the configured Environment authentication authority as the actor issuer. It requires the Identity peer on its private listener before reading the request. It accepts only application/json and limits the actual body to 2 MiB. The limit also applies when the request has no declared length or writable server limit. An oversized body returns 413 before JSON parsing or verifier creation. It forwards the authenticated actor to the fixed Orchestrator owner, reads the current runtime registration, and verifies the full reviewed-call proof. It then reads current gateway state and repeats current authority checks. The scoped reader and verifier share one process-wide managed gate instance. No route applies a gate or changes network rules. Current in-process state cannot prove forced network enforcement. Without that proof, the verifier returns a denial and no acknowledgment. The missing-enforcement response is 503 with remote_mcp_forced_network_enforcement_unavailable and Cache-Control: no-store. This route does not read credentials or contact an MCP server. It does not add an Identity credential-release success path.
RemoteMcpNativeCallContext.CreateVerified creates an in-process context from an authenticated principal, current runtime registration, and exact Orchestrator verification receipt. Its constructor is private. JSON cannot carry its authority. The trusted host supplies the configured issuer and original actor authorization. The receiving handler must check current owners again before network IO. Neither an MCP header nor an actor name in a request body replaces authentication.
IRemoteMcpManagedGatewayPolicy defines ApplyAsync, VerifyAsync, and RevokeAsync. Each method accepts RemoteMcpManagedGatewayGateDescriptor and returns RemoteMcpManagedGatewayPolicyReceipt. The descriptor pins the Environment generation, policy resource and generation, egress intent, options, gateway, target, and complete connection/configuration/discovery/catalog/tool snapshot. The receipt binds the operation, descriptor hash, handler instance, and gate state. It proves only in-process handler state. It does not prove forced network mediation.
RemoteMcpCurrentGatewayPolicyReader.ReadCurrentAsync obtains the full gateway binding from IRemoteMcpManagedGatewayStateReader, not the saved catalog reference. The handler must return its current descriptor and a fresh Verify receipt. The reader checks current actor and run authority, selected L7 options, lifecycle, owner-recorded policy generation, target purpose, intent hash, and full snapshot. It repeats authority and handler reads after waits and rejects changed state.
This reader does not render an L7 intent through the Cilium-only adapter. That adapter still rejects L7 intents, and its datapath flag remains false. VerifyForDispatchAsync reports missing forced-network proof. It cannot turn a current handler receipt or owner-recorded generation into dispatch authority. The source includes managed-handler readback and the optional current-call route. Network enforcement, gate application, and native dispatch remain unavailable.
The managed gate retains exact descriptors in process and returns fresh verification receipts. A restart loses that state; a receipt does not prove persistent or network state. The optional receiving route does not provide a native sender. Missing network enforcement or purpose evidence still denies both a send and credential delivery. Management reads retain Projects authority; these types do not expose an unauthenticated connection resolver.
This source does not prove a Registry fetch, MCP handshake, tools/list request, reviewed tool selection, native SDK use, or remote tool call. Tool acceptance, OAuth binding, and trusted transport are separate capabilities. Each one needs its own reviewed implementation and evidence.
sequenceDiagram
actor Caller
participant Environment
participant Projects as Projects & Config
participant Database as Environment PostgreSQL
Caller->>Environment: Create or update connection configuration
Environment->>Projects: Read current caller authority
Projects-->>Environment: Current permission and revisions
Environment->>Database: Write immutable revision and CAS the head
Environment->>Projects: Re-read current caller authority
Projects-->>Environment: Current permission and revisions
Environment-->>Caller: Return no-store configuration snapshot
Caller->>Environment: PUT identity-binding with expected revision/hash and operation ID
Environment->>Projects: Recheck current WriteProjects authority
Projects-->>Environment: Current actor and permission
Environment->>Database: Append Identity-bound revision; CAS head and clear catalog
Environment->>Projects: Recheck authority before commit and response
Projects-->>Environment: Current actor and permission
Environment-->>Caller: Return no-store final revision/hash receipt
Note over Environment: Parser is local and pure. These routes do not contact a Registry or MCP server
Note over Environment: The Identity reference is generated by Identity; this route does not create consent or credentials
Note over Environment: Shared call and gate contracts do not enable native sends or prove forced network mediation
Note over Environment: The current L7 binding comes from active handler readback, not a saved catalog reference
Note over Environment: A native handoff retains the original actor context and full proof; network proof remains required
Note over Environment: The Orchestrator recipient has a separate, default-off TLS listener and peer check
Note over Environment: The optional Identity current-call route checks owners and active gate state but returns no acknowledgment without network proof
Note over Environment: The caller checks its copied bytes; the receiver must still check its actual upstream request