App Connect¶
App Connect is a multi-tenant API gateway and integration platform. It lets the Nexivo platform connect to any third-party service — CRMs, ticketing systems, ERPs, and more — by registering an App Connector with a target URL and authentication config. The gateway then transparently proxies requests from AI agents (and other internal services) to those upstream APIs, handling auth injection, path rewriting, and OpenAPI-based tool discovery automatically.
Source
IdeaProjects/NHDS/app-connect — Java 25 / Spring Boot 4.0.6 / Spring Cloud Gateway WebMVC
Tech Stack¶
| Layer | Technology |
|---|---|
| Framework | Spring Boot 4.0.6 |
| Java | 25 (virtual threads enabled) |
| Gateway | Spring Cloud Gateway Server WebMVC (servlet, non-reactive) |
| Route engine | Apache Camel 4.20.0 (YAML DSL) |
| Database | PostgreSQL + Spring Data JPA + Flyway |
| Caching / Messaging | Redis Pub/Sub (cross-instance route refresh) |
| Auth | Spring Security OAuth2 Client + Keycloak Admin Client |
| Observability | OpenTelemetry + Prometheus + Spring Actuator |
Core Concepts¶
graph TD
APP[App\nOpenAPI spec + MCP config] --> AC[AppConnector\nTenant-scoped instance]
APP --> RES[Resource\nCamel route template]
RES --> RQ[ResourceQuery\nParameterised Camel route]
APP --> EV[AppEvent\nEvent definition]
AC --> MCP[MCPServer\nOAuth2-secured MCP endpoint]
| Concept | Description |
|---|---|
| App | A reusable blueprint for a third-party service — holds the OpenAPI spec, default auth config, and MCP configuration |
| AppConnector | A tenant-specific instantiation of an App — has its own URI, auth credentials, and Keycloak service account |
| Resource | A named entity exposed by the app (e.g. contacts, tickets) with a Camel YAML route template |
| ResourceQuery | A parameterised query on a Resource (e.g. search-by-email) — each has its own Camel YAML route |
| AppEvent | An event definition that can be associated with an App or Connector (inbound/outbound event schema) |
| MCPServer | An MCP (Model Context Protocol) server endpoint registered per tenant; used by Herald and Atlas to discover AI tools |
Gateway Routing¶
Every AppConnector gets a dynamic Spring Cloud Gateway route at:
Requests are processed through a fixed filter chain before being forwarded to the connector's configured upstream URI:
sequenceDiagram
participant Client
participant GW as App Connect Gateway
participant Auth as Auth Filter
participant Upstream as Connector URI
Client->>GW: GET /app-connections/{id}/contacts?q=acme
GW->>GW: Validate tenant header (if scoped)
GW->>Auth: Inject auth credentials
Auth->>Auth: Basic → Authorization: Basic …<br>OAuth2 → fetch/refresh token<br>Token → Bearer / header / query param<br>API Key → custom header
GW->>Upstream: GET /contacts?q=acme + auth headers
Upstream-->>GW: 200 JSON
GW-->>Client: 200 JSON (response logged)
Filter chain (applied in order):
- Logging — request and response body logged
- Path rewrite —
/app-connections/{id}/segment→/segment - Header filtering — only
AcceptandContent-Typeforwarded to upstream - Auth injection — credentials added based on connector's
authConfig
Routes are rebuilt at startup and on any connector create/update/delete by publishing a routes:refresh message to Redis, which triggers all running instances to reload.
Authentication Types¶
App Connect supports four auth strategies on a per-connector basis:
| Type | How it works |
|---|---|
| Basic | Encodes username:password → Authorization: Basic … |
| OAuth2 | Manages token lifecycle (fetch + refresh) via clientId, clientSecret, tokenUrl |
| Token | Attaches a pre-configured token via Bearer header, custom header, or query parameter |
| API Key | Injects a named header with a fixed key value |
Auth config is stored as JSONB on both App and AppConnector entities, allowing the connector to override the app's default.
Resource Queries (Apache Camel)¶
Resources and their queries are executed as dynamically loaded Apache Camel YAML routes. Each ResourceQuery.config field contains a Camel YAML DSL route definition. When first requested, CamelResourceRouteManager installs the route, rewrites its ID to {connectorId}/{resourceAlias}/{queryAlias}, and converts the from URI to a direct: endpoint.
Execution flow:
sequenceDiagram
participant Caller
participant SVC as AppConnectorResourcesService
participant Camel as CamelResourceRouteManager
participant Route as Camel YAML Route
Caller->>SVC: GET /app-connectors/{id}/resources/{alias}?q={query}¶m=value
SVC->>Camel: ensureQueryRouteLoaded(connectorId, resourceAlias, queryAlias)
Camel->>Route: Install route if not present
SVC->>SVC: Resolve auth headers from connector AuthConfig
SVC->>Camel: Execute route with connectorUri + params + auth headers
Camel->>Route: Run YAML DSL (HTTP call to upstream)
Route-->>SVC: Response body (JSON string)
SVC-->>Caller: JSON response
This means any HTTP interaction pattern can be expressed as a Camel YAML route without deploying new code.
REST API¶
Apps — /apps¶
| Method | Path | Purpose |
|---|---|---|
GET |
/apps |
List all registered apps |
POST |
/apps (multipart) |
Create app — body: config JSON + openapiSpec file |
POST |
/apps/{uuid}/connectors |
Create a connector from an app's default auth config |
App Connectors — /app-connectors¶
| Method | Path | Purpose |
|---|---|---|
POST |
/app-connectors (multipart) |
Create connector — config JSON + optional openapiSpec + logo |
GET |
/app-connectors |
List connectors for the current tenant |
GET |
/app-connectors/{id} |
Get connector details |
PUT |
/app-connectors/{id} |
Update name, URI, or auth config |
DELETE |
/app-connectors/{id} |
Delete connector (also removes Keycloak service account) |
PUT |
/app-connectors/{id}/openapi (multipart) |
Replace OpenAPI spec; re-syncs to Tool Management Service |
POST |
/app-connectors/{id}/resources-refresh |
Force-reload Camel routes for connector's resources |
Resources & Queries — /app-connectors/{id}/resources¶
| Method | Path | Purpose |
|---|---|---|
GET |
/{id}/resources |
List resources (connector's own + inherited from parent App) |
GET |
/{id}/resources/{alias}/queries |
List queries for a resource |
GET |
/{id}/resources/{alias}/schema |
OpenAPI schema for the resource |
GET |
/{id}/resources/{alias}?q={queryAlias}&… |
Execute a resource query |
Events — /app-connectors/{id}/events¶
| Method | Path | Purpose |
|---|---|---|
GET |
/{id}/events |
List events (connector's own + inherited from parent App) |
MCP Servers — /mcp-servers¶
| Method | Path | Purpose |
|---|---|---|
GET |
/mcp-servers/discover?serverUrl=… |
Discover OAuth endpoints from an MCP server's well-known metadata |
POST |
/mcp-servers/register-client?registrationEndpoint=…&redirectUri=… |
Dynamic Client Registration (RFC 7591) |
POST |
/mcp-servers |
Register an MCP server for the tenant |
GET |
/mcp-servers |
List MCP servers for the tenant |
GET |
/mcp-servers/{id} |
Get MCP server details |
DELETE |
/mcp-servers/{id} |
Delete MCP server |
MCP Server Management¶
App Connect is the registry for MCP (Model Context Protocol) servers — the mechanism by which AI agents (Atlas, Herald) discover and call external tools at runtime.
Registration flow:
sequenceDiagram
participant Admin
participant AC as App Connect
participant MCP as MCP Server
Admin->>AC: GET /mcp-servers/discover?serverUrl=https://mcp.example.com
AC->>MCP: GET /.well-known/oauth-protected-resource
MCP-->>AC: authorization_servers list
AC-->>Admin: OAuth endpoints (tokenUrl, authorizationUri)
Admin->>AC: POST /mcp-servers/register-client
AC->>MCP: POST {registrationEndpoint} (RFC 7591 DCR)
MCP-->>AC: clientId + clientSecret
AC-->>Admin: Credentials
Admin->>AC: POST /mcp-servers (serverUrl, clientId, clientSecret, agentId, allowedTools, …)
AC->>AC: Store MCPServer record (tenantId scoped)
Each MCPServer record holds the server URL, OAuth2 credentials, the agentId it is associated with, an allowedTools list (JSON array), and a timeout (default 90 s).
Herald and Atlas query App Connect to resolve MCP server configs for a given agentId at conversation start.
Keycloak Integration¶
When an AppConnector is created, App Connect provisions a Keycloak service account client asynchronously:
AppConnectorCreatedEventpublished synchronously on creation.AppConnectorCreatedListenerpicks it up on a background thread (tenant context preserved).- A Keycloak confidential client is created —
clientIdderived from{tenantId}-{connectorName}. - The generated
clientIdandclientSecretare written back to theAppConnectorentity. ConnectorStatustransitions:PENDING→ACTIVE(success) orERROR(failure).
On delete, AppConnectorDeletedListener removes the Keycloak client.
Tool Management Sync¶
Whenever an App or AppConnector OpenAPI spec is created or updated, App Connect calls the Tool Management Service (http://tool-management-service) via a multipart REST request. This keeps the AI agents' tool catalogue up to date without manual intervention.
Domain Model¶
AppConnector¶
| Field | Notes |
|---|---|
id |
UUID — primary key |
name |
Display name |
uri |
Upstream base URL |
authConfig |
JSONB — polymorphic: Basic / OAuth2 / Token / API Key |
tenantId |
Multi-tenancy scoping |
app |
Parent App definition (many-to-one) |
openapiSpec |
OpenAPI YAML/JSON (optional) |
clientId / clientSecret |
Keycloak-provisioned service account |
status |
PENDING · ACTIVE · INACTIVE · ERROR |
MCPServer¶
| Field | Notes |
|---|---|
id |
UUID |
serverUrl |
MCP server endpoint |
accessToken / refreshToken |
OAuth2 tokens |
clientId / clientSecret / tokenUrl / scope |
OAuth2 credentials |
agentId |
Associates server with a specific AI agent |
allowedTools |
JSONB array of permitted tool names |
timeout |
Request timeout in seconds (default 90) |
tenantId |
Multi-tenancy scoping |
active |
Enable / disable without deletion |
Configuration¶
| Variable | Default | Purpose |
|---|---|---|
spring.datasource.url |
jdbc:postgresql://postgres:5432/app_connects |
PostgreSQL URL |
spring.data.redis.host |
redis |
Redis host for route refresh pub/sub |
spring.data.redis.port |
6379 |
Redis port |
keycloak.realm |
nexivo |
Keycloak realm |
keycloak.serverUrl |
https://auth.dev.cognitry.io/ |
Keycloak server |
keycloak.clientId |
admin-cli |
Keycloak admin client |
keycloak.clientSecret |
(required) | Keycloak admin secret |
services.tool-management |
http://tool-management-service |
Tool Management Service base URL |
server.port |
8080 |
HTTP port |
DB_POOL_MAX |
50 |
Max DB pool size |
DB_POOL_MIN |
10 |
Min DB pool size |