Docs/API Reference/Developer API & MCP Server

Developer API & MCP Server

Surfaces
(1) Plain REST /api/dev/v1/**; (2) inbound MCP server /mcp; (3) A2A agent-to-agent cards and JSON-RPC. All external programmatic entry points other than the normal JWT API.
Credential
A tenant-scoped API key, Authorization: Bearer vmcp_<key>, issued from /api/mcpserver/grants. A2A uses the normal Keycloak JWT instead.
Module
core/mcpserver, active only with the Spring profile mcpserver. Without it these routes do not exist.

API keys ("grants")

A grant is a named key plus the exact agents, workflows and knowledge collections it may touch. One key system serves both the MCP server and the REST developer API. Management is McpServerAccessController, base /api/mcpserver/grants, authenticated with a normal Keycloak JWT.

Verb and pathGateBehaviour
POST /api/mcpserver/grantsPERM_MANAGE_INTEGRATIONSCreate. Body: name (required), exposedAgentIds[], exposedWorkflowIds[], exposedKnowledgeCollectionIds[]. 201 with {grant, rawApiKey}. The raw key is shown once.
GET /api/mcpserver/grantsauthenticatedList grants: id, name, apiKeyDisplayPrefix, enabled, exposed*Ids, createdAt, updatedAt, lastUsedAt. Never the key.
PUT /api/mcpserver/grants/{id}PERM_MANAGE_INTEGRATIONSReplace the exposed id sets.
PATCH /api/mcpserver/grants/{id}PERM_MANAGE_INTEGRATIONSBody {"enabled": true|false}.
POST /api/mcpserver/grants/{id}/rotatePERM_MANAGE_INTEGRATIONSNew key for the same grant; the old key stops working immediately. Returns {grant, rawApiKey}.
DELETE /api/mcpserver/grants/{id}PERM_MANAGE_INTEGRATIONSRevoke; 204.
GET /api/mcpserver/grants/{id}/tool-callsPERM_MANAGE_INTEGRATIONSTool-call log attributed to that grant (agentId, sessionId, toolName, toolInput, toolOutput, errorMessage, startedAt, completedAt); newest first. Contains raw inputs and outputs.

Keys have the prefix vmcp_ and are stored hashed (McpApiKeyHasher). Treat them like passwords; never commit or paste them.

How a key is authenticated

sequenceDiagram
  participant C as Caller
  participant F as McpApiKeyAuthFilter
  participant G as Grant service
  participant H as Handler (/mcp or /api/dev/v1)
  C->>F: Authorization: Bearer vmcp_...
  F->>G: resolve tenant schema from key
  G-->>F: schema (else 401 "Invalid API key.")
  F->>G: load grant in tenant, check hash and enabled
  F->>F: rate limit per grant (429 when exceeded)
  F->>H: TenantContext + McpCallerContext(exposed ids)
  H->>H: id in exposed set? else 403

Developer REST API (/api/dev/v1)

DeveloperApiController. Plain JSON in and out, callable with any HTTP client. Base: https://api.example.com/api/dev/v1.

Verb and pathRequestResponse
GET /agentsnone200: array of {id, name} for the agents the key exposes.
GET /workflowsnone200: array of {id, name}.
POST /agents/{agentId}/chat{message (required), sessionId?}200 {reply, sessionId}: the full reply, not streamed (chunks are joined server-side). Omitted sessionId generates dev-api-<grantId>-<uuid>. Reuse it to continue a conversation.
POST /workflows/{workflowId}/runoptional {input?} (string)200 WorkflowRunResponse (same shape as the JWT workflow API). Synchronous.
GET /workflows/{workflowId}/runs/{runId}none200 WorkflowRunResponse; 404 if the run does not belong to that workflow.

Status codes (from the controller)

CodeWhen
401 / 429Key problems / rate limit (filter, above).
403Agent or workflow id is outside the key's exposed set. Body {"error": "..."}.
422Chat message missing or blank.
404Run not found for that workflow.
502Agent call, workflow run or run-status read threw; body {"error": "Agent call failed: ..."} and similar.
curl -X POST https://api.example.com/api/dev/v1/agents/<agentId>/chat \
  -H "Authorization: Bearer vmcp_<key>" \
  -H "Content-Type: application/json" \
  -d '{"message":"Hello","sessionId":"my-session-1"}'

Streaming is not available on this surface; for token streaming use the JWT endpoint (Streaming). Rate limits other than the per-grant window above, and any per-plan quotas on developer keys, are not documented in the repository.

MCP server (/mcp)

Configuration (core/app-config/.../application-mcpserver.yml): server name vegaduta-mcp-server, version 1.0.0, type SYNC, protocol STREAMABLE HTTP, endpoint /mcp. Use any MCP client that supports streamable HTTP with the Authorization: Bearer vmcp_<key> header. The older SSE transport (GET /sse) is not what this deployment serves.

The advertised tool list is fixed and global (an application-scoped bean), so per-tenant curation is enforced inside each tool via the key's exposed sets. A tenant's agents are therefore never listed to another tenant's key.

ToolInputsEffect
list_my_agentsnoneAgents the key exposes.
call_agentagent_id (required), message (required), session_idSends a message and returns the reply; reuse session_id for follow-ups.
list_my_workflowsnoneWorkflows the key exposes.
call_workflowworkflow_id (required), inputRuns a workflow.
search_knowledgequery (required), top_k (default 5, capped at 20)Searches the exposed knowledge collections.
Outbound MCP is separate
Connecting VegaDūta agents to other MCP servers is the client side, managed at /api/mcp/registry (McpRegistryController, writes need PERM_MANAGE_INTEGRATIONS). Security posture is summarised on Threat Model.

A2A (Agent2Agent)

Verb and pathAuthPurpose
GET /.well-known/agent-card.json (legacy alias /.well-known/agent.json)nonePlatform Agent Card (A2aAgentCardController); cached 5 minutes; carries no tenant data.
GET /.well-known/jwks.jsonnoneEd25519 public key(s) for card signatures; empty key set when signing is off.
GET /api/agents/{agentId}/a2a/agent-card.jsonJWT, agent RUNNERPer-agent card; no-store.
POST /api/a2aJWTJSON-RPC 2.0 platform endpoint; target agent from params.metadata.agentId or params.message.metadata.agentId.
POST /api/agents/{agentId}/a2aJWTAgent-scoped JSON-RPC endpoint.

Other developer-facing surfaces

Evidence & references