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 profilemcpserver. 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 path | Gate | Behaviour |
|---|---|---|
POST /api/mcpserver/grants | PERM_MANAGE_INTEGRATIONS | Create. Body: name (required), exposedAgentIds[], exposedWorkflowIds[], exposedKnowledgeCollectionIds[]. 201 with {grant, rawApiKey}. The raw key is shown once. |
GET /api/mcpserver/grants | authenticated | List grants: id, name, apiKeyDisplayPrefix, enabled, exposed*Ids, createdAt, updatedAt, lastUsedAt. Never the key. |
PUT /api/mcpserver/grants/{id} | PERM_MANAGE_INTEGRATIONS | Replace the exposed id sets. |
PATCH /api/mcpserver/grants/{id} | PERM_MANAGE_INTEGRATIONS | Body {"enabled": true|false}. |
POST /api/mcpserver/grants/{id}/rotate | PERM_MANAGE_INTEGRATIONS | New key for the same grant; the old key stops working immediately. Returns {grant, rawApiKey}. |
DELETE /api/mcpserver/grants/{id} | PERM_MANAGE_INTEGRATIONS | Revoke; 204. |
GET /api/mcpserver/grants/{id}/tool-calls | PERM_MANAGE_INTEGRATIONS | Tool-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
- Missing or malformed header:
401, body{"error":"Missing or malformed Authorization: Bearer <api-key> header."}. Unknown, disabled or deleted key:401 Invalid API key. - Rate limit: fixed one-minute window per grant, default
app.mcpserver.rate-limit.max-calls-per-minute=60; exceeding returns429Rate limit exceeded - try again shortly.The limiter fails open to an in-process limit if Redis is unreachable. - The filter is registered for
/mcp/*and/api/dev/*only. Those paths sit on a dedicated Spring Security chain with no JWT processing, otherwise avmcp_key is rejected as a malformed JWT (Authentication). - Every call is audit-logged under actor
dev-api:<grantId>(actionmcp.grant.dev_api_callfor the REST surface); the synthetic role isROLE_mcp-external.
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 path | Request | Response |
|---|---|---|
GET /agents | none | 200: array of {id, name} for the agents the key exposes. |
GET /workflows | none | 200: 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}/run | optional {input?} (string) | 200 WorkflowRunResponse (same shape as the JWT workflow API). Synchronous. |
GET /workflows/{workflowId}/runs/{runId} | none | 200 WorkflowRunResponse; 404 if the run does not belong to that workflow. |
Status codes (from the controller)
| Code | When |
|---|---|
| 401 / 429 | Key problems / rate limit (filter, above). |
| 403 | Agent or workflow id is outside the key's exposed set. Body {"error": "..."}. |
| 422 | Chat message missing or blank. |
| 404 | Run not found for that workflow. |
| 502 | Agent 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.
| Tool | Inputs | Effect |
|---|---|---|
list_my_agents | none | Agents the key exposes. |
call_agent | agent_id (required), message (required), session_id | Sends a message and returns the reply; reuse session_id for follow-ups. |
list_my_workflows | none | Workflows the key exposes. |
call_workflow | workflow_id (required), input | Runs a workflow. |
search_knowledge | query (required), top_k (default 5, capped at 20) | Searches the exposed knowledge collections. |
/api/mcp/registry (McpRegistryController,
writes need PERM_MANAGE_INTEGRATIONS). Security posture is summarised on Threat Model.
A2A (Agent2Agent)
| Verb and path | Auth | Purpose |
|---|---|---|
GET /.well-known/agent-card.json (legacy alias /.well-known/agent.json) | none | Platform Agent Card (A2aAgentCardController); cached 5 minutes; carries no tenant data. |
GET /.well-known/jwks.json | none | Ed25519 public key(s) for card signatures; empty key set when signing is off. |
GET /api/agents/{agentId}/a2a/agent-card.json | JWT, agent RUNNER | Per-agent card; no-store. |
POST /api/a2a | JWT | JSON-RPC 2.0 platform endpoint; target agent from params.metadata.agentId or params.message.metadata.agentId. |
POST /api/agents/{agentId}/a2a | JWT | Agent-scoped JSON-RPC endpoint. |
- Methods (either name accepted):
SendMessage/message/send,SendStreamingMessage/message/stream(answers astext/event-stream),GetTask/tasks/get,CancelTask/tasks/cancel. Anything else is method-not-found. - Request header
A2A-Versionchooses the response dialect only. Body must be a JSON object withjsonrpc: "2.0"and amethod. - Failures are HTTP 200 with a JSON-RPC
errormember, per the binding. - Controller is on when
app.a2a.enabledis true (default true). The card'sskillslist is deliberately empty.
Other developer-facing surfaces
- Outbound event webhooks and signature verification: Webhooks & Callbacks.
- AG-UI runs for third-party front ends: Streaming.
- IDE and browser clients use the normal JWT API (client id
agentic-ai-ideis in the allowed-azplist). Such clients are not part of this repository. - The public marketing-side API reference is
web/app/api-reference/page.tsx(curated, not generated).
Evidence & references
core/mcpserver/src/main/java/com/agenticai/core/mcpserver/DeveloperApiController.java— all five/api/dev/v1routes, DTO records, status codes..../McpServerAccessController.javaanddto/*— grant lifecycle and shapes;McpApiKeyAuthFilter.java,McpApiKeyHasher.java(vmcp_),McpServerRateLimiter.java,McpServerToolConfig.java,tools/*ToolCallback.java(tool names and input schemas).core/app-config/src/main/resources/application-mcpserver.yml— server name, protocol, endpoint, rate-limit default.core/security/.../SecurityConfig.java—mcpServerFilterChain, grant-route gates.core/agents/.../a2a/A2aAgentCardController.java,A2aJsonRpcController.java,A2aVersion.java.- Threat Model; Agents & Agent Runtime (FR-AGT-09).