Skip to main content

MCP

4.8 MCP Platform

Kroov exposes a full MCP control plane: remote HTTP MCPs, catalog install, Kubernetes-backed managed runtimes, per-user OAuth vault, federation entitlements, definitions/diagnostics, and an internal runtime proxy.

SurfaceAuth
api/mcp-servers, definitions, credentialsJWT + org Owner/Admin of active tenant (X-AINexus-Tenant); servers scoped by TenantId
api/mcp-catalog, api/mcp-runtimeJWT platform Admin (+ hosted runtime attribute); control-plane only, not org catalogue
api/mcp-instancesJWT or delegation; requires hosted MCP runtimes
api/integrationsJWT (OAuth connect for end users)
api/mcp-runtime-proxyX-Mcp-Controller-Token (AllowAnonymous + token check)
api/delegations/exchangeProject API key + service/user tokens
Chat with MCP toolsProject API key (+ optional user/delegation headers)

Concepts

Transport types (transportType):

ValueNameUsage
0HttpOpenApiDeprecated — OpenAPI bridge
1McpStreamableHttpNative MCP over HTTP (preferred)
2McpStdioStdio profile (catalog custom images; not JSON-importable)

Credential types (credentialType): None, Passthrough, OAuth, Pat, EnvVar.

Remote vs managed: Remote MCPs call a vendor URL. Managed MCPs run as isolated Kubernetes worker pods; chat targets mcpInstanceId whose endpoint is routed through the internal proxy.

MCP servers — McpServerController (api/mcp-servers)

Organization Owner/Admin manage servers for the active organization. New servers receive the active tenant’s TenantId. Platform Admin without org membership cannot list or mutate org servers. Catalog/runtime Kubernetes endpoints remain platform AdminOnly.

MethodPathAuthPurpose
GET/api/mcp-serversOrg Owner/AdminList servers for active tenant
GET/api/mcp-servers/{id}Org Owner/Admin (same tenant)Detail + tools
POST / PUT / DELETE/api/mcp-servers[/{id}]Org Owner/AdminCRUD (create binds TenantId)
PUT/api/mcp-servers/{id}/tools/{toolId}Org Owner/AdminEnable/disable tool
POST/api/mcp-servers/import/previewOrg Owner/AdminPreview JSON config
POST/api/mcp-servers/importOrg Owner/AdminInstall from JSON (+ optional one-shot credential)
POST/api/mcp-servers/{id}/sync-toolsOrg Owner/AdminNative MCP tool discovery
POST/api/mcp-servers/{id}/import-openapiOrg Owner/AdminLegacy HttpOpenApi
GET / PUT/api/projects/{projectId}/mcp-serversProject access / Org Owner/AdminList assignments / replace (same-tenant servers only)
PUT/api/mcp-servers/{id}/projectsOrg Owner/AdminSet this server’s projects (same-tenant; non-destructive)

Profile fields: name, baseUrl, description, transportType, remoteEndpointUrl, registryServerName, registryVersion, stdioCommand, stdioArgsJson, stdioEnvVarKeysJson, authMode, contextMode, requireCredential, credentialType, oauthProviderKey, authHeaderName, authHeaderTemplate (must contain {credential} when required), allowedHosts, allowedPathPrefix, defaultMaxToolRounds, defaultMaxToolCalls, isActive.

Remote Streamable HTTP create:

curl -X POST https://your-kroov-host/api/mcp-servers \
-H "Authorization: Bearer $ORG_ADMIN_JWT" \
-H "X-AINexus-Tenant: $TENANT_ID" \
-H "Content-Type: application/json" \
-d '{
"name": "SerpAPI",
"baseUrl": "https://mcp.serpapi.com/{credential}/mcp",
"transportType": 1,
"remoteEndpointUrl": "https://mcp.serpapi.com/{credential}/mcp",
"credentialType": 3,
"requireCredential": true,
"authMode": 2
}'

JSON import (Claude Desktop / Cursor / VS Code mcpServers shape). Stdio/npx entries are rejected for JSON import — use Catalog → Custom for containerised MCPs.

{
"json": "{ \"mcpServers\": { \"gmail\": { \"serverUrl\": \"https://gmailmcp.googleapis.com/mcp/v1\", \"oauth\": { \"clientId\": \"…\", \"clientSecret\": \"…\" } } } }",
"credential": "optional-one-shot-pat"
}

Sync tools:

curl -X POST .../api/mcp-servers/12/sync-tools \
-H "Authorization: Bearer $ORG_ADMIN_JWT" \
-H "X-AINexus-Tenant: $TENANT_ID" \
-H "Content-Type: application/json" \
-d '{ "credential": "sk-live-..." }'

{ "synced": 15 }

Catalog — McpCatalogController (api/mcp-catalog) — AdminOnly

source query: github (default) or custom (pre-built K8s images).

MethodPathPurpose
GET/api/mcp-catalog?search=&source=&page=&pageSize=Search (auto-sync)
GET/api/mcp-catalog/{serverName}?source=Detail
POST/api/mcp-catalog/refresh?source=Force sync
POST/api/mcp-catalog/{serverName}/install?source=Install (+ tool sync for remote)

Custom install may return managedRuntime: true, runtimeProfileId, requiresPairing.

Definitions & credentials — McpDefinitionController

Under /api/mcp-servers/{id}/…:

MethodPathPurpose
GET / PUT/definitionCanonical definition
POST/definition/validateValidate
GET/definition/export?format=Export
GET/definition/revisions[/{rev}]History
POST/definition/revisions/{rev}/restoreRestore
GET/credential-requirementsWhat creds are needed
GET/credential-statusStatus for account/project
PUT / DELETE/credentials/{accountName}Vault set/delete
POST/diagnostics/{validate|discover-oauth|test-connection|list-tools}Admin diagnostics

Managed instances — McpInstanceController (api/mcp-instances)

Requires hosted MCP runtimes (Kubernetes:Enabled). Attribute [RequiresHostedMcpRuntimes]503 otherwise.

MethodPathPurpose
GET / POST/api/mcp-instancesList / create (Idempotency-Key optional)
GET/{id}Detail + availableActions
GET/{id}/pairingWhatsApp QR (Cache-Control: no-store)
POST/{id}/restart | /suspend | /resume | /reassociateLifecycle
GET / POST / DELETE/{id}/grants[/{grantId}]Instance grants
DELETE/{id}Delete worker + row
POST/{id}/runtime-statusAllowAnonymous — worker → API status callback
{ "mcpServerId": 5, "projectId": 1, "ownerUserId": 2, "ownerExternalPrincipalId": null }

Statuses: Requested, Provisioning, AwaitingPairing, Ready, Degraded, Suspended, PairingRejected, Deleting, Deleted.

Federated callers use X-AINexus-Delegation with scopes such as mcp:pair, mcp:invoke, mcp:manage.

Runtime profiles — McpRuntimeController (api/mcp-runtime) — Admin + hosted runtimes

MethodPathPurpose
POST/api/mcp-runtime/analyzePreview npx/stdio → generic runtime
POST/api/mcp-runtime/registerRegister (hostingMode: container | subprocess Dev-only)
GET/api/mcp-runtime/{id}Profile status
POST/api/mcp-runtime/{id}/rebuildKaniko rebuild

Runtime proxy — McpRuntimeProxyController (api/mcp-runtime-proxy)

Internal data-plane proxy to the worker pod via the Kubernetes API:

GET|POST|DELETE /api/mcp-runtime-proxy/{instanceId}/mcp/

Auth: header X-Mcp-Controller-Token must equal Kubernetes:ControllerToken / McpRuntime:ControllerToken.

User OAuth / PAT integrations — IntegrationAuthController (api/integrations)

MethodPathPurpose
GET/api/integrations/mineUser’s stored credentials
GET/api/integrations/availableConnectable MCP servers + connected?
GET/api/integrations/{mcpServerId}/connect?returnTo={ "authorizeUrl" }
GET/api/integrations/{provider}/callbackOAuth callback → SPA
PUT/api/integrations/{mcpServerId}/tokenStore PAT { "token": "…" }
DELETE/api/integrations/{mcpServerId}/disconnectRevoke

Provider keys include Google and GitHub. Client secrets are never returned in API responses.

Chat credential resolution order

  1. Inline delegation.credential
  2. OAuth / PAT vault for the authenticated user (/api/integrations)
  3. Managed instance via delegation.mcpInstanceId (phone match enforced for WhatsApp federated users)