handle_request
The canonical entry point. Full signature, input/output types, error model.
Every dispatched call into MAP — from the CLI, the SDKs, the HTTP gateway, the MCP server — ultimately calls MapEngine::handle_request. It lives in engine/core/src/engine.rs and is the only public method on the engine type.
Signature
impl MapEngine {
pub async fn handle_request(
&self,
input: HandleRequestInput,
) -> CoreResult<HandleRequestOutput>;
}CoreResult<T> is Result<T, CoreError> — the engine-level error type defined in the same module.
HandleRequestInput
From engine/common/src/lib.rs:
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HandleRequestInput {
/// Canonical protocol name (e.g. "MACS", "MIND")
pub protocol: String,
/// Semantic version requested. "v1.0.0" or "v1" for best-in-major.
pub version: String,
/// Operation name within the protocol (e.g. "auth_negotiation").
pub operation: String,
/// JSON-serialized request payload.
pub input: serde_json::Value,
/// Organizational tenant scope. The caller's tenant must match
/// or be on an active MOAT treaty with the target tenant.
pub tenant_id: String,
/// Pre-built context attached by the identity middleware.
/// If absent, the engine builds a minimal context from the request.
pub invoke_context: Option<map_common::InvokeContext>,
}The invoke_context field is populated upstream — typically by services/gateway/ when a request arrives over HTTP, or by the CLI when running locally. If absent, the engine constructs a minimal context but cannot resolve the caller identity beyond the DID.
HandleRequestOutput
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HandleRequestOutput {
pub output: serde_json::Value,
}The output field is the Response::data value returned by the protocol module. The Response::metadata field (if any) flows back through the audit pipeline but is not included in this output — callers retrieve metadata via MAX::traceability_graph keyed by the correlation ID.
Error model: CoreError
#[derive(thiserror::Error, Debug)]
pub enum CoreError {
#[error("unknown protocol: {0}")]
UnknownProtocol(String),
#[error("unknown version: {protocol} requested {version}")]
UnknownVersion { protocol: String, version: String },
#[error("rate limited: retry after {retry_after:?}")]
RateLimited { retry_after: std::time::Duration },
#[error("capability denied: {protocol}.{operation}")]
CapabilityDenied { protocol: String, operation: String },
#[error("circuit open for {protocol}")]
CircuitOpen { protocol: String },
#[error("no healthy endpoint for {0}")]
NoEndpointAvailable(String),
#[error("protocol error: {0}")]
ProtocolError(#[from] map_common::ProtocolError),
#[error("timeout after {0:?}")]
Timeout(std::time::Duration),
#[error("internal error: {0}")]
Internal(String),
}CoreError is mapped to HTTP status codes by the gateway:
CoreError | HTTP |
|---|---|
UnknownProtocol / UnknownVersion | 404 |
RateLimited | 429 (with Retry-After) |
CapabilityDenied | 403 |
CircuitOpen / NoEndpointAvailable | 503 |
Timeout | 504 |
ProtocolError | 422 if payload error, 502 if adapter, 500 otherwise |
Internal | 500 |
Construction
use map_core::{MapEngine, MapEngineBuilder};
use map_storage::ProductionStores;
let engine = MapEngineBuilder::new()
.protocol_registry(/* ... */)
.router(/* ... */)
.context_manager(/* ... */)
.load_balancer(/* ... */)
.security_gateway(/* ... */)
.audit_pipeline(/* ... */)
.rate_limiter(/* ... */)
.build()
.await?;The engine is constructed once at service start. All fields are Arc<...> so the engine is Clone and cheap to share across worker tasks.
Calling
use map_core::{HandleRequestInput};
use serde_json::json;
let resp = engine.handle_request(HandleRequestInput {
protocol: "MACS".into(),
version: "v1.0.0".into(),
operation: "auth_negotiation".into(),
input: json!({ "profile": "DidAuth", "challenge_kind": "Nonce" }),
tenant_id: "org_acme".into(),
invoke_context: Some(my_ctx),
}).await?;use map_sdk::Client;
let client = Client::from_env()?;
let resp = client
.dispatch("MACS", "v1.0.0", "auth_negotiation", json!({
"profile": "DidAuth",
"challenge_kind": "Nonce"
}))
.await?;POST /v1/dispatch HTTP/2
Host: api.multiagentic.dev
Authorization: Bearer $MAP_API_KEY
X-Agent-Did: did:oas:l1fe:agent:0xa3f...
Content-Type: application/json
{
"protocol": "MACS",
"version": "v1.0.0",
"operation": "auth_negotiation",
"input": { "profile": "DidAuth", "challenge_kind": "Nonce" },
"tenant_id": "org_acme"
}Tests
The engine entry point has full coverage in engine/core/src/tests.rs (716 lines). Notable test groups:
pipeline_stages— validates each of the 8 stages independently and in sequencerate_limiter_behavior— token bucket exhaustion, refill, multi-tenant isolationsecurity_gating— hierarchical wildcard matching (exact / protocol-wildcard / global-wildcard)circuit_breaker— CLOSED → OPEN → HALF_OPEN transitions with backoffaudit_pipeline_async— confirms audit writes never block the response
Run them locally: cargo test -p map-core from the workspace root.