MCP Ingresses
An MCP ingress is an ingress with type: mcp. It exposes one compiled Reactor component as a typed tool on Hantera's shared MCP server at /mcp.
An MCP ingress is a logical endpoint rather than a separate URL:
MCP client
→ POST /mcp (tools/call)
→ MCP ingress selected by generated tool name
→ Reactor component invoked with typed arguments
→ component result returned as MCP tool contentUse MCP ingresses to make deliberate business capabilities available to an agent. The component remains reusable by jobs, rules, HTTP ingresses, and other app logic; only the MCP ingress exposes it as a tool.
Define an MCP ingress
An MCP ingress can be declared by an app or managed directly as a tenant resource.
Declare the ingress in h_app.yaml:
id: reports
name: Reports
components:
- id: reports/revenue-summary.hrc
ingresses:
- id: revenue-summary
componentId: reports/revenue-summary.hrc
type: mcp
properties:
title: Revenue summary
description: Summarize revenue for a date range and optional sales channel.
execution:
authority: acl
acl:
- graph/order:query
- graph/order:field:createdAt
- graph/order:field:status
- graph/order:field:orderTotal
- graph/order:field:currencyCodeAfter installing or activating the app in development mode, this ingress is available to authorized MCP clients as apps_reports_revenue_summary.
All ingress reads return the canonical execution object. For compatibility, a direct ingress API write may use the legacy root-level acl field instead; do not send both acl and execution in the same request.
Tool name and presentation
Tool names are deterministic and cannot be configured. Every character outside a-z, A-Z, 0-9, and _ becomes _.
| Ingress origin | Ingress ID | Generated MCP tool name |
|---|---|---|
| App | apps/reports/revenue-summary | apps_reports_revenue_summary |
| Tenant resource | reports/revenue-summary | tool_reports_revenue_summary |
Tool names are limited to 64 characters. The system_ prefix is reserved for Hantera's built-in tools and cannot be used by an ingress.
properties.title and properties.description control how the tool is presented to MCP clients:
| Field | Limit | Fallback |
|---|---|---|
title | 64 characters | Component display name, then component ID |
description | 4096 characters | Component description, then Invokes the '<ingress id>' ingress. |
Configured blank or overlong values are rejected; omit a field to use its fallback.
Authorization and execution authority
Two independent decisions protect an MCP ingress:
- Can the MCP caller invoke this ingress? The session needs
mcp/tools/ingress/{ingressId}:call.mcp/tools/ingress:callgrants access to all ingress tools;mcp/tools:callcovers both ingress and built-in tools. - Which permissions does the component receive while it runs?
executionselects the component's effective authority.
execution:
authority: acl | caller | restricted-caller
acl: []| Authority | Component authority | ACL requirement |
|---|---|---|
acl | Exactly the ingress ACL | Optional; an omitted ACL is empty. This is the default. |
caller | Exactly the caller's effective authority | Must be absent. |
restricted-caller | Caller authority intersected with the ingress ACL | Required and non-empty. |
acl is the safest default: a caller can invoke the tool but does not automatically pass all its authority into the component. Use caller only for a deliberately user-scoped tool. Use restricted-caller when the component must remain inside both the caller's authority and a defined data-access ceiling.
See Execution Authority for the shared ingress model, legacy ACL compatibility, and transport restrictions.
execution:
authority: restricted-caller
acl:
- graph/order:query
- graph/order:field:createdAt
- graph/order:field:orderTotalThis ingress runs with caller authority ∩ ingress ACL.
Component contract
The MCP server derives an input schema from the Reactor component's param declarations and an output schema from its result. Required parameters are parameters without defaults.
Supported input and result shapes include text, number, boolean, UUID, instant, date, lists, records, maps, and compatible unions. A nothing union branch maps to JSON null.
MCP tool arguments are JSON objects, so tuple parameters are not supported. Durations, functions, inferred/intersection types, and component contracts deeper than 32 schema levels are also rejected. Streaming results are unsupported in the current version.
Tool structured output is always an object:
- Record and map results are returned directly.
- Other scalar or list results are wrapped as
{ "result": ... }. - Multiple
fromvalues are returned as an array underresult.
An ingress with an invalid component target or non-representable contract is hidden from MCP discovery until it is corrected.
Open a Portal view from an MCP tool
Set properties.portal: true to attach Hantera's reusable Portal MCP App to an ingress tool. The attachment lets MCP hosts that support Apps render a Portal view after a successful tool call.
properties:
title: Search orders
description: Search orders and open the matching list in Portal.
portal: trueportal is presentation metadata only. It does not change the ingress's generated name, call authorization, execution authority, input schema, or result schema. The tool must still return useful structured content and text fallback for MCP clients that do not support Apps.
Choose the Portal destination in the result
Enabling the Portal App does not select a destination by itself. The Reactor component chooses the view for each successful call by returning a portal descriptor:
param phrase: text
from {
phrase = phrase
portal = {
viewPath = '/sales/orders'
query = { phrase = phrase }
}
}| Field | Required | Description |
|---|---|---|
portal.viewPath | Yes | An internal Portal route, such as /sales/orders, /order, or an installed app view. It must be a single-slash relative path: no URI scheme, ?, #, or //. |
portal.query | No | A map whose keys and values become URL query parameters. Values are stringified and URL-encoded. |
A missing or invalid descriptor does not make the tool invocation fail; an Apps-capable host shows no actionable Portal destination. A missing view or unavailable app view is handled as ordinary Portal navigation.
Security boundary
The Portal App is navigation, not a data channel. The MCP bearer token is never passed to the Portal. The standalone Portal host performs its own sign-in flow, and the view loads data using the signed-in Portal user's normal session permissions. Setting portal: true never grants the MCP caller or Portal user additional access.
Collisions and visibility
Two MCP ingresses must not expose the same generated name.
- Two colliding MCP ingresses in one app manifest block app installation and development activation.
- A collision across active resources — for example, across apps or tenant-managed ingresses — does not block configuration.
- In an active-set collision, Hantera hides every conflicting tool. It never picks a winner.
- Each conflicting ingress emits a warning signal at
ingresses/{id}/tool.
Unauthorized, inactive, invalid, and colliding tools are all absent from tools/list. An attempt to call one is intentionally indistinguishable from calling an unknown tool.
Verify an ingress
- Install the app or create the tenant ingress.
- Authorize an MCP client with
mcp/tools/ingress:callormcp/tools:call, or grant the exactmcp/tools/ingress/{ingressId}:callpermission. - Reconnect the client after role or scope changes.
- Run
tools/listand find the generated name. - Invoke it with
tools/calland anargumentsobject matching the component parameters.
Related
- Model Context Protocol — connect and authorize an MCP client.
- MCP API reference — endpoint, protocol, and tool calls.
- Ingresses — the common ingress model.
- Execution Authority — authority modes and ACL compatibility.
- Components — Reactor components and their execution contexts.