MAP Docs
Engine

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:

CoreErrorHTTP
UnknownProtocol / UnknownVersion404
RateLimited429 (with Retry-After)
CapabilityDenied403
CircuitOpen / NoEndpointAvailable503
Timeout504
ProtocolError422 if payload error, 502 if adapter, 500 otherwise
Internal500

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 sequence
  • rate_limiter_behavior — token bucket exhaustion, refill, multi-tenant isolation
  • security_gating — hierarchical wildcard matching (exact / protocol-wildcard / global-wildcard)
  • circuit_breaker — CLOSED → OPEN → HALF_OPEN transitions with backoff
  • audit_pipeline_async — confirms audit writes never block the response

Run them locally: cargo test -p map-core from the workspace root.

On this page