Aller au contenu principal

Universal API Contract: Define Once, Expose as REST + MCP

· 8 minutes de lecture
STOA Team
The STOA Platform Team

You define an API once. STOA exposes it as both a REST endpoint and an MCP tool — same policies, same monitoring, zero duplication. That is the Universal API Contract (UAC), and this tutorial walks you through it in 5 minutes.

Most API platforms force you to maintain separate configurations for each protocol: one for REST consumers, another for AI agents via MCP. That means duplicated rate limits, duplicated auth rules, and twice the surface area for misconfiguration. UAC eliminates that.

What You'll Build

By the end of this tutorial, you'll have:

  1. A single UAC contract defining a weather API
  2. A REST endpoint serving traditional HTTP clients
  3. An MCP tool serving AI agents — from the same contract
  4. A shared rate-limit policy that applies to both protocols

Time: 5 minutes Difficulty: Beginner Prerequisites: Running STOA instance (Quick Start), curl

Configure your environment
export STOA_API_URL="http://localhost:8000"      # Control Plane API
export STOA_GATEWAY_URL="http://localhost:3001" # Gateway endpoint

Using the hosted version? Replace with https://api.<YOUR_DOMAIN> and https://mcp.<YOUR_DOMAIN>.

Step 1: Get an Admin Token

First, authenticate with the Control Plane API:

TOKEN=$(curl -s -X POST http://localhost:8080/realms/stoa/protocol/openid-connect/token \
-d "client_id=control-plane-api" \
-d "client_secret=your-client-secret" \
-d "grant_type=client_credentials" | jq -r .access_token)

You'll use this token for all subsequent API calls.

Step 2: Create a UAC Contract

A UAC contract is the single source of truth for your API. It defines the backend, authentication, policies, and portal visibility — all in one place.

CONTRACT_RESPONSE=$(curl -s -X POST "${STOA_API_URL}/v1/contracts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"apiName": "weather-api",
"apiVersion": "1.0.0",
"tenant": "default",
"displayName": "Weather API",
"description": "Current weather data for any city",
"endpoint": {
"url": "https://api.open-meteo.com/v1",
"method": "REST",
"timeout": "10s"
},
"auth": {
"type": "api_key"
},
"policies": [
{
"type": "rate_limit",
"config": {
"requests_per_minute": 60
}
}
],
"portal": {
"visible": true,
"categories": ["weather", "demo"]
}
}')

CONTRACT_ID=$(echo $CONTRACT_RESPONSE | jq -r .id)
echo "Contract ID: $CONTRACT_ID"

That one payload defines everything: the backend URL, authentication method, rate limiting, and portal listing. No gateway-specific configuration needed.

Step 3: Check the Default Protocol Bindings

When you create a UAC contract, STOA automatically enables a REST binding. Check what bindings are active:

curl -s "${STOA_API_URL}/v1/contracts/${CONTRACT_ID}/bindings" \
-H "Authorization: Bearer $TOKEN" | jq .

Expected output:

{
"bindings": [
{
"protocol": "rest",
"enabled": true,
"path": "/weather-api/v1",
"created_at": "2026-02-24T10:00:00Z"
}
]
}

REST is active by default. Your API is already reachable through the gateway at /weather-api/v1. But we want AI agents to access it too — so let's add MCP.

Step 4: Enable the MCP Binding

Add an MCP binding so AI agents can discover and call your API as an MCP tool:

curl -s -X POST "${STOA_API_URL}/v1/contracts/${CONTRACT_ID}/bindings" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"protocol": "mcp",
"enabled": true,
"tool_name": "get_weather",
"tool_description": "Get current weather for a city"
}' | jq .

Now the same contract is exposed over two protocols. The rate limit of 60 requests/minute applies to both — no separate policy needed.

Step 5: Test the REST Endpoint

Call the weather API through the gateway's REST path:

curl -s "${STOA_GATEWAY_URL}/weather-api/v1/forecast?latitude=48.85&longitude=2.35" \
-H "X-API-Key: your-api-key" | jq '.current_weather'

You should see weather data for Paris. The request passed through STOA's middleware pipeline: authentication, rate limiting, logging — all defined in your UAC contract.

Step 6: Test the MCP Tool

Now call the same API as an MCP tool. AI agents use this format to discover and invoke tools:

# List available tools (MCP discovery)
curl -s "${STOA_GATEWAY_URL}/mcp/tools/list" \
-H "Authorization: Bearer $TOKEN" | jq '.tools[] | select(.name == "get_weather")'

Expected output:

{
"name": "get_weather",
"description": "Get current weather for a city",
"inputSchema": {
"type": "object",
"properties": {
"latitude": { "type": "number" },
"longitude": { "type": "number" }
}
}
}

Call the tool:

curl -s -X POST "${STOA_GATEWAY_URL}/mcp/tools/call" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "get_weather",
"arguments": {
"latitude": 48.85,
"longitude": 2.35
}
}' | jq .

Same backend. Same rate limit. Same audit trail. Two protocols.

Step 7: Verify Shared Policies

The rate limit applies across both protocols. Let's verify by checking the policy attached to the contract:

curl -s "${STOA_API_URL}/v1/contracts/${CONTRACT_ID}" \
-H "Authorization: Bearer $TOKEN" | jq '.policies'
[
{
"type": "rate_limit",
"config": {
"requests_per_minute": 60
},
"applied_to": ["rest", "mcp"]
}
]

The applied_to field confirms that both bindings share the policy. If a consumer burns 50 requests via REST, they have 10 left for MCP calls — it's a single quota pool.


What You've Built

In 5 minutes, you've experienced STOA's core value proposition:

WhatTraditional ApproachUAC Approach
API definitionOne config per gatewayOne UAC contract
Protocol supportSeparate REST + MCP configsSingle contract, multiple bindings
Rate limitingDuplicate policies per protocolShared policy, single quota
MonitoringSeparate dashboardsUnified audit trail

This is the "Define Once, Expose Everywhere" model. As STOA adds protocol bindings (GraphQL and gRPC are on the roadmap), your existing contracts gain new capabilities without reconfiguration.


Real-World Scenario: A SaaS With Human and AI Consumers

Imagine you run a logistics SaaS. Your REST API serves your web dashboard and mobile app. Now a customer wants their AI assistant to query shipment status automatically.

Without UAC, you'd build and maintain a separate MCP integration: new routes, new auth config, new rate limits, new monitoring. When you update the shipment schema, you update it in two places. When you change rate limits, you change them in two places. When something breaks, you debug two systems.

With UAC, you add one line — the MCP binding — and your existing contract, policies, and monitoring cover both use cases. The AI assistant gets the same auth, the same rate limits, and the same audit trail as your web dashboard. Schema changes propagate to both protocols automatically because there's only one source of truth.

This is not a hypothetical. As AI agents become first-class API consumers alongside humans, the platforms that treat them as just another protocol binding will scale. The ones that maintain parallel API stacks will not.


How UAC Works Under the Hood

The UAC is a normalized, gateway-agnostic API definition. When you create a contract, STOA's control plane stores it as the source of truth. When you add bindings, the Gateway Adapter translates the UAC into whatever format the target gateway needs.

           ┌──── REST binding ──── HTTP clients
UAC ───────┤
└──── MCP binding ──── AI agents (Claude, GPT, etc.)

If you later connect a Kong or Gravitee gateway, the same UAC contract syncs to their native format automatically. See the architecture overview for the full picture.

The key insight is that protocol bindings are views of the contract, not copies. There's no synchronization problem because there's nothing to synchronize — one contract, multiple read paths.

What Happens When You Change Something?

ActionEffect on RESTEffect on MCP
Update backend URLImmediateImmediate
Change rate limitApplies to REST requestsApplies to MCP tool calls
Rotate API keyREST calls need new keyMCP auth unchanged (uses OAuth)
Add a new field to the schemaAvailable in REST responseAvailable in MCP tool output
Disable the APIREST returns 404MCP tool disappears from listing

No dual maintenance. No drift between configurations.


FAQ

Can I add GraphQL or gRPC bindings?

Not yet. REST and MCP are currently supported. GraphQL and gRPC bindings are planned — check the roadmap for timeline. When they ship, your existing UAC contracts will support them without changes.

How is this different from creating two separate APIs?

Two separate APIs mean two sets of policies, two monitoring streams, and two points of failure. With UAC, you manage one contract. Changes to rate limits, auth rules, or backend URLs propagate to all protocol bindings instantly.

What about authentication?

UAC supports oauth2, api_key, basic, and none auth types. The auth configuration applies across all bindings. For protocol-specific auth (e.g., MCP OAuth 2.1 with PKCE), STOA handles the translation at the gateway layer.

Can I use UAC with my existing Kong or Apigee gateway?

Yes. STOA's adapter pattern supports 7 gateway backends, including Kong, Gravitee, Apigee, Azure APIM, and AWS API Gateway. The UAC contract is translated to each gateway's native format.


Next Steps