From e8b913793c813de938715c00361991b5aa00f110 Mon Sep 17 00:00:00 2001 From: Piumal Rathnayake Date: Wed, 5 Aug 2026 07:53:12 +0530 Subject: [PATCH 1/4] Update seed script --- portals/api-portal/scripts/seed-samples.ps1 | 5 +++-- portals/api-portal/scripts/seed-samples.sh | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/portals/api-portal/scripts/seed-samples.ps1 b/portals/api-portal/scripts/seed-samples.ps1 index 9e834eb21..6c809b694 100644 --- a/portals/api-portal/scripts/seed-samples.ps1 +++ b/portals/api-portal/scripts/seed-samples.ps1 @@ -71,6 +71,7 @@ if (Test-Path -LiteralPath (Join-Path $RootDir 'resources\samples')) { $ApiPortalUrl = if ($env:API_PORTAL_URL) { $env:API_PORTAL_URL } else { 'https://localhost:9543' } $PlatformApiUrl = if ($env:PLATFORM_API_URL) { $env:PLATFORM_API_URL } else { 'https://localhost:9243' } +$ApiPortalApiBase = '/api-portal/api/v0.9' # Colors only when writing to an interactive terminal (respects the NO_COLOR # convention: https://no-color.org/) — a piped/CI log gets plain ASCII symbols @@ -249,7 +250,7 @@ function Invoke-SeedEntry([string]$SampleDir, [string]$Endpoint) { $definition = Get-ChildItem -LiteralPath $SampleDir -Filter 'definition.*' -File -ErrorAction SilentlyContinue | Sort-Object Name | Select-Object -First 1 - $curlArgs = @('-sk', '-X', 'POST', "$ApiPortalUrl/api/v0.9/$Endpoint", + $curlArgs = @('-sk', '-X', 'POST', "$ApiPortalUrl$ApiPortalApiBase/$Endpoint", '-H', $AuthHeader, '-F', "metadata=@$apiYaml;type=application/yaml") if ($definition) { @@ -268,7 +269,7 @@ function Invoke-SeedEntry([string]$SampleDir, [string]$Endpoint) { $id = $null try { $id = ($body | ConvertFrom-Json).id } catch { $id = $null } $docs = @{ Result = ''; Failed = $false } - if ($id) { $docs = Invoke-SeedDocs $SampleDir "/api/v0.9/$Endpoint/$id" } + if ($id) { $docs = Invoke-SeedDocs $SampleDir "$ApiPortalApiBase/$Endpoint/$id" } if ($docs.Failed) { # Entry itself was created, but its docs upload failed — surface diff --git a/portals/api-portal/scripts/seed-samples.sh b/portals/api-portal/scripts/seed-samples.sh index ae32b3247..76776a1df 100755 --- a/portals/api-portal/scripts/seed-samples.sh +++ b/portals/api-portal/scripts/seed-samples.sh @@ -65,6 +65,7 @@ fi API_PORTAL_URL="${API_PORTAL_URL:-https://localhost:9543}" PLATFORM_API_URL="${PLATFORM_API_URL:-https://localhost:9243}" +API_PORTAL_API_BASE="/api-portal/api/v0.9" # Colors/symbols only when writing to an interactive terminal (respects the # NO_COLOR convention: https://no-color.org/) — a piped/CI log gets plain @@ -187,7 +188,7 @@ seed_entry() { local definition definition=$(compgen -G "$sample_dir/definition.*" 2>/dev/null | head -1 || true) - local curl_args=(-sk -X POST "$API_PORTAL_URL/api/v0.9/$endpoint" \ + local curl_args=(-sk -X POST "$API_PORTAL_URL$API_PORTAL_API_BASE/$endpoint" \ -H "$AUTH_HEADER" \ -F "metadata=@$api_yaml;type=application/yaml") if [ -n "$definition" ]; then @@ -203,7 +204,7 @@ seed_entry() { id=$(echo "$body" | jq -r '.id // empty') DOCS_RESULT="" DOCS_FAILED=0 - [ -n "$id" ] && seed_docs "$sample_dir" "/api/v0.9/$endpoint/$id" + [ -n "$id" ] && seed_docs "$sample_dir" "$API_PORTAL_API_BASE/$endpoint/$id" if [ "$DOCS_FAILED" -eq 1 ]; then # Entry itself was created, but its docs upload failed — surface this # as a failure (red symbol, FAILED tally) rather than a clean success, From 1cd78856bc73dba04ea68f37a2ee4a3da9d1a5a4 Mon Sep 17 00:00:00 2001 From: Piumal Rathnayake Date: Wed, 5 Aug 2026 07:53:23 +0530 Subject: [PATCH 2/4] Update docs --- portals/api-portal/distribution/README.md | 4 ++-- .../docs/administer/api-token-curl.md | 2 +- .../api-portal/docs/administer/design-mode.md | 8 ++++---- .../administer/key-manager-integration.md | 10 +++++----- .../docs/administer/manage-organizations.md | 6 +++--- .../docs/administer/manage-views.md | 20 +++++++++---------- .../docs/administer/subscription-plans.md | 12 +++++------ .../docs/administer/webhook-integration.md | 14 ++++++------- .../docs/introduction/quick-start.md | 6 +++--- .../docs/publish-apis/api-content-and-docs.md | 8 ++++---- .../docs/publish-apis/manage-api-workflows.md | 12 +++++------ .../docs/publish-apis/publishing-apis.md | 18 ++++++++--------- 12 files changed, 60 insertions(+), 60 deletions(-) diff --git a/portals/api-portal/distribution/README.md b/portals/api-portal/distribution/README.md index 336074f5a..a9401fffb 100644 --- a/portals/api-portal/distribution/README.md +++ b/portals/api-portal/distribution/README.md @@ -73,7 +73,7 @@ Verify the Platform API is healthy: curl -fk https://localhost:9243/health ``` -Open the API Portal in a browser at `https://localhost:9543/default/views/default` and log in with the admin credentials printed by `setup.sh`. +Open the API Portal in a browser at `https://localhost:9543/api-portal/default/views/default` and log in with the admin credentials printed by `setup.sh`. > **Browser trust warning?** Both services use a self-signed TLS certificate by default. Click **Advanced → Proceed** to continue. See [Custom TLS Certificates](#custom-tls-certificates) to remove the warning permanently. @@ -133,7 +133,7 @@ Like AI Workspace, it's an opt-in you turn on by editing `configs/config.toml` 1. **Copy the `[api_portal.design_mode]` block** from the "DESIGN MODE CONFIGURATION" section of the shipped `configs/config-template.toml` into `configs/config.toml` (keep `enabled = true`). The sample paths are already correct for the bundled samples — leave them as-is. 2. **Restart the API Portal:** `docker compose up` (or `docker compose restart api-portal`). -The portal then serves from disk at `/views/default` (e.g. `http://localhost:9543/views/default`). Because design mode never touches the database, the accompanying Platform API and its database go unused while it's on — set `enabled` back to `false` and restart to return to the normal, database-backed portal. +The portal then serves from disk at `/views/default` (e.g. `http://localhost:9543/api-portal/views/default`). Because design mode never touches the database, the accompanying Platform API and its database go unused while it's on — set `enabled` back to `false` and restart to return to the normal, database-backed portal. The sample content lives in **`resources/samples/`** (`apis/`, `mcps/`, `applications.yaml`, `subscription-plans.yaml`), which the API Portal container mounts at `/app/samples`. To preview **your own** APIs and MCP servers, add or edit files there and restart — no image rebuild needed. diff --git a/portals/api-portal/docs/administer/api-token-curl.md b/portals/api-portal/docs/administer/api-token-curl.md index b74603155..995f6a778 100644 --- a/portals/api-portal/docs/administer/api-token-curl.md +++ b/portals/api-portal/docs/administer/api-token-curl.md @@ -118,7 +118,7 @@ echo "TOKEN=$TOKEN" ```bash # The org is resolved from the token's org claim (set via ORGANIZATION_IDENTIFIER # during login in Step 3) — no org identifier needed in the request itself. -BASE="https://localhost:9543/api/v0.9" +BASE="https://localhost:9543/api-portal/api/v0.9" # List APIs curl -sk "${BASE}/apis" -H "Authorization: Bearer $TOKEN" | jq . diff --git a/portals/api-portal/docs/administer/design-mode.md b/portals/api-portal/docs/administer/design-mode.md index 9b3a04d2a..f20ad2562 100644 --- a/portals/api-portal/docs/administer/design-mode.md +++ b/portals/api-portal/docs/administer/design-mode.md @@ -30,7 +30,7 @@ Then start the portal normally: npm start ``` -Visit **http://localhost:9543/views/default**. +Visit **http://localhost:9543/api-portal/views/default**. > The portal always starts on plain HTTP in design mode — no TLS certificate setup required. @@ -143,7 +143,7 @@ TOKEN=$(curl -sk -X POST "https://localhost:9243/api/portal/v0.9/auth/login" \ Initial upload: ```bash -curl -X POST "http://localhost:9543/api/v0.9/views/{viewName}/layout" \ +curl -X POST "http://localhost:9543/api-portal/api/v0.9/views/{viewName}/layout" \ -H "Authorization: Bearer $TOKEN" \ -F "zipFile=@my-theme.zip" ``` @@ -151,7 +151,7 @@ curl -X POST "http://localhost:9543/api/v0.9/views/{viewName}/layout" \ Update an existing layout: ```bash -curl -X PUT "http://localhost:9543/api/v0.9/views/{viewName}/layout" \ +curl -X PUT "http://localhost:9543/api-portal/api/v0.9/views/{viewName}/layout" \ -H "Authorization: Bearer $TOKEN" \ -F "zipFile=@my-theme.zip" ``` @@ -159,7 +159,7 @@ curl -X PUT "http://localhost:9543/api/v0.9/views/{viewName}/layout" \ Revert to the default layout: ```bash -curl -X DELETE "http://localhost:9543/api/v0.9/views/{viewName}/layout/template" \ +curl -X DELETE "http://localhost:9543/api-portal/api/v0.9/views/{viewName}/layout/template" \ -H "Authorization: Bearer $TOKEN" ``` diff --git a/portals/api-portal/docs/administer/key-manager-integration.md b/portals/api-portal/docs/administer/key-manager-integration.md index 06e3eaea7..4d217e9d1 100644 --- a/portals/api-portal/docs/administer/key-manager-integration.md +++ b/portals/api-portal/docs/administer/key-manager-integration.md @@ -31,7 +31,7 @@ spec: ``` ```bash -curl -k -X POST https://localhost:9543/api/v0.9/key-managers \ +curl -k -X POST https://localhost:9543/api-portal/api/v0.9/key-managers \ -H "Authorization: Bearer $TOKEN" \ -F "keymanager=@keymanager.yaml" ``` @@ -48,13 +48,13 @@ Every key manager is treated as a generic OAuth2 `client_credentials` provider. ## List Key Managers ```bash -curl -k https://localhost:9543/api/v0.9/key-managers -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/key-managers -H "Authorization: Bearer $TOKEN" ``` ## Get a Key Manager ```bash -curl -k https://localhost:9543/api/v0.9/key-managers/{kmId} -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/key-managers/{kmId} -H "Authorization: Bearer $TOKEN" ``` ## Update a Key Manager @@ -72,7 +72,7 @@ spec: ``` ```bash -curl -k -X PUT https://localhost:9543/api/v0.9/key-managers/{kmId} \ +curl -k -X PUT https://localhost:9543/api-portal/api/v0.9/key-managers/{kmId} \ -H "Authorization: Bearer $TOKEN" \ -F "keymanager=@keymanager-update.yaml" ``` @@ -80,7 +80,7 @@ curl -k -X PUT https://localhost:9543/api/v0.9/key-managers/{kmId} \ ## Delete a Key Manager ```bash -curl -k -X DELETE https://localhost:9543/api/v0.9/key-managers/{kmId} \ +curl -k -X DELETE https://localhost:9543/api-portal/api/v0.9/key-managers/{kmId} \ -H "Authorization: Bearer $TOKEN" ``` diff --git a/portals/api-portal/docs/administer/manage-organizations.md b/portals/api-portal/docs/administer/manage-organizations.md index f3ad5f6e7..0569202e3 100644 --- a/portals/api-portal/docs/administer/manage-organizations.md +++ b/portals/api-portal/docs/administer/manage-organizations.md @@ -55,7 +55,7 @@ They remain in the OpenAPI spec, and the code behind them is intact, so they can ## Read the Organization ```bash -curl -k https://localhost:9543/api/v0.9/organizations/acme -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/organizations/acme -H "Authorization: Bearer $TOKEN" ``` ## Update the Organization @@ -75,7 +75,7 @@ spec: ``` ```bash -curl -k -X PUT https://localhost:9543/api/v0.9/organizations/acme \ +curl -k -X PUT https://localhost:9543/api-portal/api/v0.9/organizations/acme \ -H "Authorization: Bearer $TOKEN" \ -F "organization=@org-update.yaml" ``` @@ -184,7 +184,7 @@ For scripts and CLI tools, get a Bearer token directly from the Platform API and TOKEN=$(curl -sk -X POST "https://localhost:9243/api/portal/v0.9/auth/login" \ -d "username=&password=" | jq -r .token) -curl -sk -H "Authorization: Bearer $TOKEN" https://localhost:9543/api/v0.9/organizations/acme +curl -sk -H "Authorization: Bearer $TOKEN" https://localhost:9543/api-portal/api/v0.9/organizations/acme ``` The token is verified locally by the API Portal against the Platform API's RS256 public key (`auth.local.public_key_path`), with no extra call to the Platform API per request. diff --git a/portals/api-portal/docs/administer/manage-views.md b/portals/api-portal/docs/administer/manage-views.md index 8f7f2a254..85a5aef12 100644 --- a/portals/api-portal/docs/administer/manage-views.md +++ b/portals/api-portal/docs/administer/manage-views.md @@ -26,7 +26,7 @@ https:////views/ ``` ```bash -curl -k -X POST https://localhost:9543/api/v0.9/views \ +curl -k -X POST https://localhost:9543/api-portal/api/v0.9/views \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ --data-binary @view.json @@ -41,13 +41,13 @@ curl -k -X POST https://localhost:9543/api/v0.9/views \ ## List Views ```bash -curl -k https://localhost:9543/api/v0.9/views -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/views -H "Authorization: Bearer $TOKEN" ``` ## Get a View ```bash -curl -k https://localhost:9543/api/v0.9/views/{viewId} -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/views/{viewId} -H "Authorization: Bearer $TOKEN" ``` ## Update a View @@ -63,7 +63,7 @@ The update request takes the full desired label set via `labels` — labels pres ``` ```bash -curl -k -X PUT https://localhost:9543/api/v0.9/views/{viewId} \ +curl -k -X PUT https://localhost:9543/api-portal/api/v0.9/views/{viewId} \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ --data-binary @view-update.json @@ -72,7 +72,7 @@ curl -k -X PUT https://localhost:9543/api/v0.9/views/{viewId} \ ## Delete a View ```bash -curl -k -X DELETE https://localhost:9543/api/v0.9/views/{viewId} -H "Authorization: Bearer $TOKEN" +curl -k -X DELETE https://localhost:9543/api-portal/api/v0.9/views/{viewId} -H "Authorization: Bearer $TOKEN" ``` --- @@ -94,7 +94,7 @@ Labels are created one at a time as a JSON object: ``` ```bash -curl -k -X POST https://localhost:9543/api/v0.9/labels \ +curl -k -X POST https://localhost:9543/api-portal/api/v0.9/labels \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ --data-binary @label.json @@ -108,13 +108,13 @@ curl -k -X POST https://localhost:9543/api/v0.9/labels \ ### List Labels ```bash -curl -k https://localhost:9543/api/v0.9/labels -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/labels -H "Authorization: Bearer $TOKEN" ``` ### Get a Label ```bash -curl -k https://localhost:9543/api/v0.9/labels/{labelId} -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/labels/{labelId} -H "Authorization: Bearer $TOKEN" ``` ### Update a Label @@ -128,7 +128,7 @@ curl -k https://localhost:9543/api/v0.9/labels/{labelId} -H "Authorization: Bear ``` ```bash -curl -k -X PUT https://localhost:9543/api/v0.9/labels/{labelId} \ +curl -k -X PUT https://localhost:9543/api-portal/api/v0.9/labels/{labelId} \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ --data-binary @label-update.json @@ -137,7 +137,7 @@ curl -k -X PUT https://localhost:9543/api/v0.9/labels/{labelId} \ ### Delete a Label ```bash -curl -k -X DELETE "https://localhost:9543/api/v0.9/labels/{labelId}" \ +curl -k -X DELETE "https://localhost:9543/api-portal/api/v0.9/labels/{labelId}" \ -H "Authorization: Bearer $TOKEN" ``` diff --git a/portals/api-portal/docs/administer/subscription-plans.md b/portals/api-portal/docs/administer/subscription-plans.md index 0aaff21de..4816a1b21 100644 --- a/portals/api-portal/docs/administer/subscription-plans.md +++ b/portals/api-portal/docs/administer/subscription-plans.md @@ -46,7 +46,7 @@ spec: ``` ```bash -curl -k -X POST https://localhost:9543/api/v0.9/subscription-plans \ +curl -k -X POST https://localhost:9543/api-portal/api/v0.9/subscription-plans \ -H "Authorization: Bearer $TOKEN" \ -F "subscriptionPlan=@plan.yaml" ``` @@ -89,7 +89,7 @@ items: ``` ```bash -curl -k -X POST https://localhost:9543/api/v0.9/subscription-plans \ +curl -k -X POST https://localhost:9543/api-portal/api/v0.9/subscription-plans \ -H "Authorization: Bearer $TOKEN" \ -F "subscriptionPlan=@plans.yaml" ``` @@ -97,13 +97,13 @@ curl -k -X POST https://localhost:9543/api/v0.9/subscription-plans \ ## List Subscription Plans ```bash -curl -k https://localhost:9543/api/v0.9/subscription-plans -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/subscription-plans -H "Authorization: Bearer $TOKEN" ``` ## Get a Subscription Plan ```bash -curl -k https://localhost:9543/api/v0.9/subscription-plans/{planId} \ +curl -k https://localhost:9543/api-portal/api/v0.9/subscription-plans/{planId} \ -H "Authorization: Bearer $TOKEN" ``` @@ -125,7 +125,7 @@ spec: ``` ```bash -curl -k -X PUT https://localhost:9543/api/v0.9/subscription-plans \ +curl -k -X PUT https://localhost:9543/api-portal/api/v0.9/subscription-plans \ -H "Authorization: Bearer $TOKEN" \ -F "subscriptionPlan=@plan-update.yaml" ``` @@ -133,7 +133,7 @@ curl -k -X PUT https://localhost:9543/api/v0.9/subscription-plans \ ## Delete a Subscription Plan ```bash -curl -k -X DELETE "https://localhost:9543/api/v0.9/subscription-plans/{planId}" \ +curl -k -X DELETE "https://localhost:9543/api-portal/api/v0.9/subscription-plans/{planId}" \ -H "Authorization: Bearer $TOKEN" ``` diff --git a/portals/api-portal/docs/administer/webhook-integration.md b/portals/api-portal/docs/administer/webhook-integration.md index 5f8465d9b..d164c1338 100644 --- a/portals/api-portal/docs/administer/webhook-integration.md +++ b/portals/api-portal/docs/administer/webhook-integration.md @@ -54,7 +54,7 @@ signature_tolerance_sec = 300 ### Create a subscriber ```bash -curl -k -X POST "https://localhost:9543/api/v0.9/webhook-subscribers" \ +curl -k -X POST "https://localhost:9543/api-portal/api/v0.9/webhook-subscribers" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ @@ -88,18 +88,18 @@ The response never includes the secret. That one `secret` value does double duty ```bash # List -curl -k "https://localhost:9543/api/v0.9/webhook-subscribers" -H "Authorization: Bearer $TOKEN" +curl -k "https://localhost:9543/api-portal/api/v0.9/webhook-subscribers" -H "Authorization: Bearer $TOKEN" # Get one -curl -k "https://localhost:9543/api/v0.9/webhook-subscribers/{subscriberId}" -H "Authorization: Bearer $TOKEN" +curl -k "https://localhost:9543/api-portal/api/v0.9/webhook-subscribers/{subscriberId}" -H "Authorization: Bearer $TOKEN" # Update (only supplied fields are changed; omitted fields keep their stored values) -curl -k -X PUT "https://localhost:9543/api/v0.9/webhook-subscribers/{subscriberId}" \ +curl -k -X PUT "https://localhost:9543/api-portal/api/v0.9/webhook-subscribers/{subscriberId}" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"enabled": false}' # Delete -curl -k -X DELETE "https://localhost:9543/api/v0.9/webhook-subscribers/{subscriberId}" \ +curl -k -X DELETE "https://localhost:9543/api-portal/api/v0.9/webhook-subscribers/{subscriberId}" \ -H "Authorization: Bearer $TOKEN" ``` @@ -606,11 +606,11 @@ Each delivery is attempted exactly once. If your subscriber endpoint is unavaila ### List recent events ```bash -curl -k https://localhost:9543/api/v0.9/webhook-events -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/webhook-events -H "Authorization: Bearer $TOKEN" ``` ### Get event details ```bash -curl -k https://localhost:9543/api/v0.9/webhook-events/{eventId} -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/webhook-events/{eventId} -H "Authorization: Bearer $TOKEN" ``` diff --git a/portals/api-portal/docs/introduction/quick-start.md b/portals/api-portal/docs/introduction/quick-start.md index c50572e03..613a87198 100644 --- a/portals/api-portal/docs/introduction/quick-start.md +++ b/portals/api-portal/docs/introduction/quick-start.md @@ -48,7 +48,7 @@ This starts the API Portal (SQLite by default). On first boot the database schem Navigate to: ``` -https://localhost:9543/default/views/default +https://localhost:9543/api-portal/default/views/default ``` Sign in with the admin username and password you set when running `./scripts/setup.sh`. @@ -245,7 +245,7 @@ TOKEN=$(curl -sk -X POST "https://localhost:9243/api/portal/v0.9/auth/login" \ -d "username=&password=" | jq -r .token) # Publish the API (the token's org_handle claim scopes this to the "default" org) -curl -sk -X POST "https://localhost:9543/api/v0.9/apis" \ +curl -sk -X POST "https://localhost:9543/api-portal/api/v0.9/apis" \ -H "Authorization: Bearer $TOKEN" \ -F "metadata=@api.yaml;type=application/yaml" \ -F "definition=@openapi.yaml;type=application/yaml" @@ -259,7 +259,7 @@ Refresh the portal — the Reading-List-API now appears in the catalog. Click it |---|---| | Organization | `default` | | Default view | `default` | -| Portal URL | `https://localhost:9543/default/views/default` | +| Portal URL | `https://localhost:9543/api-portal/default/views/default` | | Admin credentials | Set when you ran `./scripts/setup.sh` (stored bcrypt-hashed in `api-platform.env`) | | Sample API | `Reading-List-API` visible in the catalog | diff --git a/portals/api-portal/docs/publish-apis/api-content-and-docs.md b/portals/api-portal/docs/publish-apis/api-content-and-docs.md index a788e0a2f..1b7e1004b 100644 --- a/portals/api-portal/docs/publish-apis/api-content-and-docs.md +++ b/portals/api-portal/docs/publish-apis/api-content-and-docs.md @@ -34,7 +34,7 @@ At least one of `web/` or `docs/` must be present in the ZIP. ```bash curl -k -X POST \ - "https://localhost:9543/api/v0.9/apis/{apiId}/assets" \ + "https://localhost:9543/api-portal/api/v0.9/apis/{apiId}/assets" \ -H "Authorization: Bearer $TOKEN" \ -F "content=@my-api-content.zip" \ -F 'imageMetadata={"api-icon":"api-icon.png"}' @@ -44,7 +44,7 @@ To update existing content, use `PUT`: ```bash curl -k -X PUT \ - "https://localhost:9543/api/v0.9/apis/{apiId}/assets" \ + "https://localhost:9543/api-portal/api/v0.9/apis/{apiId}/assets" \ -H "Authorization: Bearer $TOKEN" \ -F "content=@my-api-content-v2.zip" \ -F 'imageMetadata={"api-icon":"api-icon.png"}' @@ -138,7 +138,7 @@ To link to externally hosted documentation (rather than uploaded files), pass `d ```bash curl -k -X POST \ - "https://localhost:9543/api/v0.9/apis/{apiId}/assets" \ + "https://localhost:9543/api-portal/api/v0.9/apis/{apiId}/assets" \ -H "Authorization: Bearer $TOKEN" \ -F "content=@my-api-content.zip" \ -F 'docMetadata=[{"name":"External Guide","url":"https://docs.example.com/guide","type":"LINK"}]' @@ -149,7 +149,7 @@ curl -k -X POST \ Retrieving content requires both a `type` (`web`, `document`, `image`, or `link`) and a `fileName` query parameter to select a single stored file: ```bash -curl -k "https://localhost:9543/api/v0.9/apis/{apiId}/assets?type=document&fileName=getting-started.md" \ +curl -k "https://localhost:9543/api-portal/api/v0.9/apis/{apiId}/assets?type=document&fileName=getting-started.md" \ -H "Authorization: Bearer $TOKEN" ``` diff --git a/portals/api-portal/docs/publish-apis/manage-api-workflows.md b/portals/api-portal/docs/publish-apis/manage-api-workflows.md index 9b7eb57ab..dc399ff0e 100644 --- a/portals/api-portal/docs/publish-apis/manage-api-workflows.md +++ b/portals/api-portal/docs/publish-apis/manage-api-workflows.md @@ -71,7 +71,7 @@ API workflow requests are JSON. The `apiWorkflowDefinition` field contains the A ```bash curl -k -X POST \ - "https://localhost:9543/api/v0.9/views/{viewId}/api-workflows" \ + "https://localhost:9543/api-portal/api/v0.9/views/{viewId}/api-workflows" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ --data-binary @workflow.json @@ -91,14 +91,14 @@ curl -k -X POST \ ## List Workflows ```bash -curl -k https://localhost:9543/api/v0.9/views/{viewId}/api-workflows \ +curl -k https://localhost:9543/api-portal/api/v0.9/views/{viewId}/api-workflows \ -H "Authorization: Bearer $TOKEN" ``` ## Get a Workflow ```bash -curl -k https://localhost:9543/api/v0.9/views/{viewId}/api-workflows/{apiWorkflowId} \ +curl -k https://localhost:9543/api-portal/api/v0.9/views/{viewId}/api-workflows/{apiWorkflowId} \ -H "Authorization: Bearer $TOKEN" ``` @@ -115,7 +115,7 @@ curl -k https://localhost:9543/api/v0.9/views/{viewId}/api-workflows/{apiWorkflo ```bash curl -k -X PUT \ - "https://localhost:9543/api/v0.9/views/{viewId}/api-workflows/{apiWorkflowId}" \ + "https://localhost:9543/api-portal/api/v0.9/views/{viewId}/api-workflows/{apiWorkflowId}" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ --data-binary @workflow-update.json @@ -125,7 +125,7 @@ curl -k -X PUT \ ```bash curl -k -X DELETE \ - "https://localhost:9543/api/v0.9/views/{viewId}/api-workflows/{apiWorkflowId}" \ + "https://localhost:9543/api-portal/api/v0.9/views/{viewId}/api-workflows/{apiWorkflowId}" \ -H "Authorization: Bearer $TOKEN" ``` @@ -149,7 +149,7 @@ The portal can generate a suggested agent prompt for a workflow using AI: ```bash curl -k -X POST \ - "https://localhost:9543/api/v0.9/views/{viewId}/api-workflows/generate-prompt" \ + "https://localhost:9543/api-portal/api/v0.9/views/{viewId}/api-workflows/generate-prompt" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ --data-binary @generate-prompt.json diff --git a/portals/api-portal/docs/publish-apis/publishing-apis.md b/portals/api-portal/docs/publish-apis/publishing-apis.md index e237ae859..3e323b95e 100644 --- a/portals/api-portal/docs/publish-apis/publishing-apis.md +++ b/portals/api-portal/docs/publish-apis/publishing-apis.md @@ -105,7 +105,7 @@ Send the manifest and definition together as a multipart upload: ```bash # REST API with OpenAPI definition -curl -k -X POST "https://localhost:9543/api/v0.9/apis" \ +curl -k -X POST "https://localhost:9543/api-portal/api/v0.9/apis" \ -H "Authorization: Bearer $TOKEN" \ -F "metadata=@api.yaml" \ -F "definition=@openapi.yaml;type=application/yaml" @@ -113,7 +113,7 @@ curl -k -X POST "https://localhost:9543/api/v0.9/apis" \ ```bash # GraphQL API -curl -k -X POST "https://localhost:9543/api/v0.9/apis" \ +curl -k -X POST "https://localhost:9543/api-portal/api/v0.9/apis" \ -H "Authorization: Bearer $TOKEN" \ -F "metadata=@api.yaml" \ -F "definition=@schema.graphql;type=application/graphql" @@ -122,7 +122,7 @@ curl -k -X POST "https://localhost:9543/api/v0.9/apis" \ ```bash # MCP server (note: MCP servers are created under /mcp-servers, not /apis). # An MCP server's contract is its tools schema (schemaDefinition) — it has no apiDefinition. -curl -k -X POST "https://localhost:9543/api/v0.9/mcp-servers" \ +curl -k -X POST "https://localhost:9543/api-portal/api/v0.9/mcp-servers" \ -H "Authorization: Bearer $TOKEN" \ -F "metadata=@mcp.yaml" \ -F "definition=@schemaDefinition.yaml;type=application/yaml" @@ -154,7 +154,7 @@ The response includes the `apiId` needed for subsequent steps. ```bash # OpenAPI YAML curl -k -X PUT \ - "https://localhost:9543/api/v0.9/apis/{apiId}" \ + "https://localhost:9543/api-portal/api/v0.9/apis/{apiId}" \ -H "Authorization: Bearer $TOKEN" \ -F 'metadata={"id":"{apiId}","name":"Order API","endPoints":{"productionURL":"https://api.example.com/orders","sandboxURL":"https://sandbox.example.com/orders"}}' \ -F "definition=@openapi.yaml;type=application/yaml" @@ -163,7 +163,7 @@ curl -k -X PUT \ ```bash # AsyncAPI YAML curl -k -X PUT \ - "https://localhost:9543/api/v0.9/apis/{apiId}" \ + "https://localhost:9543/api-portal/api/v0.9/apis/{apiId}" \ -H "Authorization: Bearer $TOKEN" \ -F 'metadata={"id":"{apiId}","name":"Order API","endPoints":{"productionURL":"https://api.example.com/orders","sandboxURL":"https://sandbox.example.com/orders"}}' \ -F "definition=@asyncapi.yaml;type=application/yaml" @@ -303,7 +303,7 @@ spec: ``` ```bash -curl -k -X PUT https://localhost:9543/api/v0.9/apis/{apiId} \ +curl -k -X PUT https://localhost:9543/api-portal/api/v0.9/apis/{apiId} \ -H "Authorization: Bearer $TOKEN" \ -F "metadata=@api-update.yaml" ``` @@ -311,7 +311,7 @@ curl -k -X PUT https://localhost:9543/api/v0.9/apis/{apiId} \ ## Delete an API ```bash -curl -k -X DELETE https://localhost:9543/api/v0.9/apis/{apiId} \ +curl -k -X DELETE https://localhost:9543/api-portal/api/v0.9/apis/{apiId} \ -H "Authorization: Bearer $TOKEN" ``` @@ -320,13 +320,13 @@ curl -k -X DELETE https://localhost:9543/api/v0.9/apis/{apiId} \ ## List APIs ```bash -curl -k https://localhost:9543/api/v0.9/apis -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/apis -H "Authorization: Bearer $TOKEN" ``` ## Get an API ```bash -curl -k https://localhost:9543/api/v0.9/apis/{apiId} -H "Authorization: Bearer $TOKEN" +curl -k https://localhost:9543/api-portal/api/v0.9/apis/{apiId} -H "Authorization: Bearer $TOKEN" ``` ## Related From c43aec9779c715c2bafb1c34da7b9d4f2720eb9f Mon Sep 17 00:00:00 2001 From: Piumal Rathnayake Date: Wed, 5 Aug 2026 09:13:08 +0530 Subject: [PATCH 3/4] Update samples and docs --- .../api-portal/docs/administer/design-mode.md | 6 +- .../apis/chat-service-api-v1.0/api.yaml | 37 ++++++ .../apis/chat-service-api-v1.0/definition.yml | 44 +++++++ .../docs/getting-started.md | 64 ++++++++++ .../samples/apis/countries-api-v1.0/api.yaml | 37 ++++++ .../countries-api-v1.0/definition.graphql | 102 ++++++++++++++++ .../docs/getting-started.md | 104 +++++++++++++++++ .../apis/countries-graphql-v1.0/api.yaml | 34 ------ .../countries-graphql-v1.0/definition.graphql | 64 ---------- .../docs/getting-started.md | 41 ------- .../samples/apis/navigation-ws-v1.0/api.yaml | 37 ------ .../apis/navigation-ws-v1.0/definition.yml | 110 ------------------ .../docs/getting-started.md | 38 ------ .../mcps/everything-mcp-server-v1.0/api.yaml | 37 ++++++ .../definition.yaml | 92 +++++++++++++++ .../docs/getting-started.md | 64 ++++++++++ .../mcps/travel-assistant-mcp-v1/api.yaml | 36 ------ .../travel-assistant-mcp-v1/definition.yaml | 109 ----------------- .../docs/getting-started.md | 6 - 19 files changed, 584 insertions(+), 478 deletions(-) create mode 100644 portals/api-portal/samples/apis/chat-service-api-v1.0/api.yaml create mode 100644 portals/api-portal/samples/apis/chat-service-api-v1.0/definition.yml create mode 100644 portals/api-portal/samples/apis/chat-service-api-v1.0/docs/getting-started.md create mode 100644 portals/api-portal/samples/apis/countries-api-v1.0/api.yaml create mode 100644 portals/api-portal/samples/apis/countries-api-v1.0/definition.graphql create mode 100644 portals/api-portal/samples/apis/countries-api-v1.0/docs/getting-started.md delete mode 100644 portals/api-portal/samples/apis/countries-graphql-v1.0/api.yaml delete mode 100644 portals/api-portal/samples/apis/countries-graphql-v1.0/definition.graphql delete mode 100644 portals/api-portal/samples/apis/countries-graphql-v1.0/docs/getting-started.md delete mode 100644 portals/api-portal/samples/apis/navigation-ws-v1.0/api.yaml delete mode 100644 portals/api-portal/samples/apis/navigation-ws-v1.0/definition.yml delete mode 100644 portals/api-portal/samples/apis/navigation-ws-v1.0/docs/getting-started.md create mode 100644 portals/api-portal/samples/mcps/everything-mcp-server-v1.0/api.yaml create mode 100644 portals/api-portal/samples/mcps/everything-mcp-server-v1.0/definition.yaml create mode 100644 portals/api-portal/samples/mcps/everything-mcp-server-v1.0/docs/getting-started.md delete mode 100644 portals/api-portal/samples/mcps/travel-assistant-mcp-v1/api.yaml delete mode 100644 portals/api-portal/samples/mcps/travel-assistant-mcp-v1/definition.yaml delete mode 100644 portals/api-portal/samples/mcps/travel-assistant-mcp-v1/docs/getting-started.md diff --git a/portals/api-portal/docs/administer/design-mode.md b/portals/api-portal/docs/administer/design-mode.md index f20ad2562..da8c39f46 100644 --- a/portals/api-portal/docs/administer/design-mode.md +++ b/portals/api-portal/docs/administer/design-mode.md @@ -176,9 +176,9 @@ samples/ │ │ └── docs/ │ └── … └── mcps/ # MCP servers → /views/default/mcps - ├── travel-assistant-mcp-v1/ + ├── everything-mcp-server-v1.0/ │ ├── api.yaml - │ ├── schemaDefinition.yaml + │ ├── definition.yaml │ └── docs/ └── … ``` @@ -237,7 +237,7 @@ spec: productionUrl: https://mcp.example.com ``` -The `schemaDefinition.yaml` alongside `api.yaml` defines the tools, resources, and prompts exposed by the server: +The `definition.yaml` alongside `api.yaml` defines the tools, resources, and prompts exposed by the server: ```yaml - type: TOOL diff --git a/portals/api-portal/samples/apis/chat-service-api-v1.0/api.yaml b/portals/api-portal/samples/apis/chat-service-api-v1.0/api.yaml new file mode 100644 index 000000000..9b8280bea --- /dev/null +++ b/portals/api-portal/samples/apis/chat-service-api-v1.0/api.yaml @@ -0,0 +1,37 @@ +apiVersion: api-portal.api-platform.wso2.com/v1 +kind: WS + +metadata: + name: chat-service-api-v1.0 + +spec: + type: WS + displayName: Chat Service API + version: v1.0 + description: Real-time chat between multiple users over a WebSocket connection. Open a socket, send a message frame, and every other connected client receives it. + status: PUBLISHED + + tags: + - websocket + - chat + - real-time + - async + + labels: + - default + + subscriptionPlans: + - Gold + - Silver + + agentVisibility: VISIBLE + + businessInformation: + businessOwner: API Team + businessOwnerEmail: api-team@example.com + technicalOwner: Platform Team + technicalOwnerEmail: platform-team@example.com + + endpoints: + productionUrl: wss://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/chat-service-api/v1.0 + sandboxUrl: wss://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/chat-service-api/v1.0 diff --git a/portals/api-portal/samples/apis/chat-service-api-v1.0/definition.yml b/portals/api-portal/samples/apis/chat-service-api-v1.0/definition.yml new file mode 100644 index 000000000..8d60537e7 --- /dev/null +++ b/portals/api-portal/samples/apis/chat-service-api-v1.0/definition.yml @@ -0,0 +1,44 @@ +asyncapi: '2.0.0' + +info: + title: Chat Service API + version: 1.0.0 + description: Real-time chat between multiple users over a WebSocket connection. + +servers: + production: + url: wss://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/chat-service-api/v1.0 + protocol: wss + description: Production WebSocket server + +channels: + /: + description: Chat channel. Messages sent by any client are broadcast to every other connected client. + publish: + summary: Send a message to the chat room + operationId: SendMessage + message: + $ref: '#/components/messages/message' + subscribe: + summary: Receive messages from the chat room + operationId: ReceiveMessage + message: + $ref: '#/components/messages/message' + +components: + messages: + message: + name: message + title: message + summary: A chat frame, sent in either direction over the same socket. + payload: + type: object + properties: + type: + type: string + description: Type of the message + example: connect + message: + type: string + description: Message content + example: Hello from user1 diff --git a/portals/api-portal/samples/apis/chat-service-api-v1.0/docs/getting-started.md b/portals/api-portal/samples/apis/chat-service-api-v1.0/docs/getting-started.md new file mode 100644 index 000000000..9d3014c6a --- /dev/null +++ b/portals/api-portal/samples/apis/chat-service-api-v1.0/docs/getting-started.md @@ -0,0 +1,64 @@ +# Getting Started + +The Chat Service API is a WebSocket API: one long-lived connection carries messages in both +directions. Anything you send is broadcast to every other client connected to the same room, and +anything they send arrives on your socket. + +## Endpoint + +``` +wss://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/chat-service-api/v1.0 +``` + +There is a single channel (`/`) — the whole API is that one socket. Unlike a REST API there are no +paths or methods to choose between. + +## Message format + +Every frame, in either direction, is a JSON object with two fields: + +| Field | Type | Description | +|---|---|---| +| `type` | string | Frame type, e.g. `connect` or `message` | +| `message` | string | The message content | + +```json +{ "type": "message", "message": "Hello from user1" } +``` + +## Connecting + +With [`websocat`](https://github.com/vi/websocat) from a terminal — type a JSON frame and press +enter to send it; incoming frames print as they arrive: + +```bash +websocat "wss://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/chat-service-api/v1.0" +``` + +From the browser: + +```js +const socket = new WebSocket( + 'wss://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/chat-service-api/v1.0' +); + +socket.onopen = () => { + socket.send(JSON.stringify({ type: 'connect', message: 'user1 joined' })); +}; + +socket.onmessage = (event) => { + const frame = JSON.parse(event.data); + console.log(`${frame.type}: ${frame.message}`); +}; +``` + +## Trying it from the portal + +Open the API's **Specification** page and switch on **Try it** to get a console that opens the +socket for you — useful for watching the broadcast behaviour without writing a client. Open it in +two browser tabs to see a message sent from one arrive in the other. + +## Source + +The backing service is published at +[wso2/api-platform-samples/chat-service-api](https://github.com/wso2/api-platform-samples/tree/main/chat-service-api). diff --git a/portals/api-portal/samples/apis/countries-api-v1.0/api.yaml b/portals/api-portal/samples/apis/countries-api-v1.0/api.yaml new file mode 100644 index 000000000..ff37d726a --- /dev/null +++ b/portals/api-portal/samples/apis/countries-api-v1.0/api.yaml @@ -0,0 +1,37 @@ +apiVersion: api-portal.api-platform.wso2.com/v1 +kind: GraphQL + +metadata: + name: countries-api-v1.0 + +spec: + type: GRAPHQL + displayName: Countries API + version: v1.0 + description: Query countries, continents, languages, and currencies in a single GraphQL request. Filter by continent, currency, or language code and select exactly the fields you need. + status: PUBLISHED + + tags: + - graphql + - countries + - geography + - reference-data + + labels: + - default + + subscriptionPlans: + - Silver + - Bronze + + agentVisibility: VISIBLE + + businessInformation: + businessOwner: API Team + businessOwnerEmail: api-team@example.com + technicalOwner: Platform Team + technicalOwnerEmail: platform-team@example.com + + endpoints: + productionUrl: https://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/countries-service-nodejs/v1.0 + sandboxUrl: https://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/countries-service-nodejs/v1.0 diff --git a/portals/api-portal/samples/apis/countries-api-v1.0/definition.graphql b/portals/api-portal/samples/apis/countries-api-v1.0/definition.graphql new file mode 100644 index 000000000..9476c1a3b --- /dev/null +++ b/portals/api-portal/samples/apis/countries-api-v1.0/definition.graphql @@ -0,0 +1,102 @@ +"Marks the field, argument, input field or enum value as deprecated" +directive @deprecated( + "The reason for the deprecation" + reason: String = "No longer supported" + ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION + +"Directs the executor to include this field or fragment only when the `if` argument is true" +directive @include( + "Included when true." + if: Boolean! + ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Indicates exactly one field must be supplied and this field must not be `null`." +directive @oneOf on INPUT_OBJECT + +"Directs the executor to skip this field or fragment when the `if` argument is true." +directive @skip( + "Skipped when true." + if: Boolean! + ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Exposes a URL that specifies the behaviour of this scalar." +directive @specifiedBy( + "The URL that specifies the behaviour of this scalar." + url: String! + ) on SCALAR + +type Continent { + code: ID! + countries: [Country!]! + name: String! +} + +type Country { + awsRegion: String! + capital: String + code: ID! + continent: Continent! + currencies: [String!]! + currency: String + emoji: String! + emojiU: String! + languages: [Language!]! + name(lang: String): String! + native: String! + phone: String! + phones: [String!]! + states: [State!]! + subdivisions: [Subdivision!]! +} + +type Language { + code: ID! + countries: [Country!]! + name: String! + native: String! + rtl: Boolean! +} + +type Query { + continent(code: ID!): Continent + continents(filter: ContinentFilterInput = {}): [Continent!]! + countries(filter: CountryFilterInput = {}): [Country!]! + country(code: ID!): Country + language(code: ID!): Language + languages(filter: LanguageFilterInput = {}): [Language!]! +} + +type State { + code: String + country: Country! + name: String! +} + +type Subdivision { + code: ID! + emoji: String + name: String! +} + +input ContinentFilterInput { + code: StringQueryOperatorInput +} + +input CountryFilterInput { + code: StringQueryOperatorInput + continent: StringQueryOperatorInput + currency: StringQueryOperatorInput + name: StringQueryOperatorInput +} + +input LanguageFilterInput { + code: StringQueryOperatorInput +} + +input StringQueryOperatorInput { + eq: String + in: [String!] + ne: String + nin: [String!] + regex: String +} diff --git a/portals/api-portal/samples/apis/countries-api-v1.0/docs/getting-started.md b/portals/api-portal/samples/apis/countries-api-v1.0/docs/getting-started.md new file mode 100644 index 000000000..f8cb02638 --- /dev/null +++ b/portals/api-portal/samples/apis/countries-api-v1.0/docs/getting-started.md @@ -0,0 +1,104 @@ +# Getting Started + +The Countries API returns country, continent, and language reference data over GraphQL. A single +request returns exactly the fields you ask for, so there is no need to stitch together several +REST calls or discard fields you did not want. + +## Endpoint + +``` +https://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/countries-service-nodejs/v1.0 +``` + +## Your first query + +Every field below is optional — ask for the ones you need and nothing else. + +```graphql +query { + countries(filter: { continent: { eq: "EU" } }) { + code + name + capital + currency + emoji + languages { + name + } + } +} +``` + +Send it with `curl`: + +```bash +curl -X POST "https://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/countries-service-nodejs/v1.0" \ + -H "Content-Type: application/json" \ + -d '{"query":"{ countries(filter:{continent:{eq:\"EU\"}}){ code name capital currency } }"}' +``` + +## What you can query + +| Query | Returns | +|---|---| +| `countries(filter:)` | Every country, optionally filtered | +| `country(code:)` | One country by ISO 3166-1 alpha-2 code, e.g. `LK` | +| `continents(filter:)` | Every continent | +| `continent(code:)` | One continent by code, e.g. `EU` | +| `languages(filter:)` | Every language | +| `language(code:)` | One language by ISO 639-1 code, e.g. `en` | + +This API is read-only — the schema declares no mutations. + +## Filtering + +`countries`, `continents`, and `languages` each take a filter whose fields accept `eq`, `ne`, +`in`, `nin`, and `regex`: + +```graphql +query { + countries(filter: { currency: { eq: "EUR" } }) { + name + currency + } +} +``` + +Combine filters to narrow further — this returns European countries that use the euro: + +```graphql +query { + countries(filter: { continent: { eq: "EU" }, currency: { eq: "EUR" } }) { + name + capital + } +} +``` + +## Looking up a single record + +Use `country`, `continent`, or `language` when you already know the code. They return a single +object rather than a list: + +```graphql +query { + country(code: "LK") { + name + native + capital + currency + continent { + name + } + languages { + code + name + } + } +} +``` + +## Source + +The backing service is published at +[wso2/api-platform-samples/countries-api](https://github.com/wso2/api-platform-samples/tree/main/countries-api). diff --git a/portals/api-portal/samples/apis/countries-graphql-v1.0/api.yaml b/portals/api-portal/samples/apis/countries-graphql-v1.0/api.yaml deleted file mode 100644 index b280bc129..000000000 --- a/portals/api-portal/samples/apis/countries-graphql-v1.0/api.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: api-portal.api-platform.wso2.com/v1 -kind: GraphQL - -metadata: - name: countries-graphql-v1.0 - -spec: - type: GRAPHQL - displayName: Countries GraphQL API - version: v1.0 - description: Explore country data — continents, languages, currencies, and more — via a public GraphQL endpoint. - status: PUBLISHED - - tags: - - graphql - - countries - - geography - - labels: - - default - - subscriptionPlans: [] - - agentVisibility: VISIBLE - - businessInformation: - businessOwner: Trevor Blades - businessOwnerEmail: hello@trevorblades.com - technicalOwner: Trevor Blades - technicalOwnerEmail: hello@trevorblades.com - - endpoints: - productionUrl: https://countries.trevorblades.com/graphql - sandboxUrl: https://countries.trevorblades.com/graphql diff --git a/portals/api-portal/samples/apis/countries-graphql-v1.0/definition.graphql b/portals/api-portal/samples/apis/countries-graphql-v1.0/definition.graphql deleted file mode 100644 index fa87cd4c3..000000000 --- a/portals/api-portal/samples/apis/countries-graphql-v1.0/definition.graphql +++ /dev/null @@ -1,64 +0,0 @@ -type Query { - countries(filter: CountryFilterInput): [Country!]! - country(code: ID!): Country - continents(filter: ContinentFilterInput): [Continent!]! - continent(code: ID!): Continent - languages(filter: LanguageFilterInput): [Language!]! - language(code: ID!): Language -} - -type Country { - code: ID! - name: String! - native: String! - phone: String! - continent: Continent! - capital: String - currency: String - languages: [Language!]! - emoji: String! - emojiU: String! - states: [State!]! -} - -type Continent { - code: ID! - name: String! - countries: [Country!]! -} - -type Language { - code: ID! - name: String - native: String - rtl: Boolean! -} - -type State { - code: String - name: String! - country: Country! -} - -input CountryFilterInput { - code: StringQueryOperatorInput - continent: StringQueryOperatorInput - currency: StringQueryOperatorInput -} - -input ContinentFilterInput { - code: StringQueryOperatorInput -} - -input LanguageFilterInput { - code: StringQueryOperatorInput -} - -input StringQueryOperatorInput { - eq: ID - ne: ID - in: [ID] - nin: [ID] - regex: String - glob: String -} diff --git a/portals/api-portal/samples/apis/countries-graphql-v1.0/docs/getting-started.md b/portals/api-portal/samples/apis/countries-graphql-v1.0/docs/getting-started.md deleted file mode 100644 index 558a2550b..000000000 --- a/portals/api-portal/samples/apis/countries-graphql-v1.0/docs/getting-started.md +++ /dev/null @@ -1,41 +0,0 @@ -# Getting Started - -The Countries GraphQL API lets you query country, continent, and language data in a single request. - -## Example query - -```graphql -query { - countries(filter: { continent: { eq: "EU" } }) { - code - name - capital - currency - emoji - languages { - name - } - } -} -``` - -Send the query to the production endpoint: - -```bash -curl -X POST https://countries.trevorblades.com/graphql \ - -H "Content-Type: application/json" \ - -d '{"query":"{ countries(filter:{continent:{eq:\"EU\"}}){ code name capital } }"}' -``` - -## Filtering - -All list fields accept an optional `filter` argument with a `StringQueryOperatorInput`: - -| Operator | Description | -|----------|-------------| -| `eq` | Exact match | -| `ne` | Not equal | -| `in` | Match any of the given values | -| `nin` | Exclude all of the given values | -| `regex` | Regular expression match | -| `glob` | Glob pattern match | diff --git a/portals/api-portal/samples/apis/navigation-ws-v1.0/api.yaml b/portals/api-portal/samples/apis/navigation-ws-v1.0/api.yaml deleted file mode 100644 index d4ca51ba7..000000000 --- a/portals/api-portal/samples/apis/navigation-ws-v1.0/api.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: api-portal.api-platform.wso2.com/v1 -kind: WS - -metadata: - name: navigation-ws-v1.0 - -spec: - type: WS - displayName: Navigation API - version: v1.0 - description: Real-time vehicle navigation and live route tracking over WebSocket. Send an origin and destination; receive turn-by-turn updates as the route is calculated and re-routed. - status: PUBLISHED - - tags: - - websocket - - navigation - - real-time - - async - - labels: - - default - - subscriptionPlans: - - Gold - - Silver - - agentVisibility: VISIBLE - - businessInformation: - businessOwner: Platform Owner - businessOwnerEmail: support@example.com - technicalOwner: API Team - technicalOwnerEmail: architecture@example.com - - endpoints: - productionUrl: wss://navigation.example.com/ws - sandboxUrl: ws://localhost:8081/ws diff --git a/portals/api-portal/samples/apis/navigation-ws-v1.0/definition.yml b/portals/api-portal/samples/apis/navigation-ws-v1.0/definition.yml deleted file mode 100644 index f87a88daf..000000000 --- a/portals/api-portal/samples/apis/navigation-ws-v1.0/definition.yml +++ /dev/null @@ -1,110 +0,0 @@ -asyncapi: 2.6.0 -info: - title: Navigation API - version: v1.0 - description: Real-time vehicle navigation and live route tracking over WebSocket. - -servers: - production: - url: wss://navigation.example.com/ws - protocol: wss - description: Production WebSocket server - sandbox: - url: ws://localhost:8081/ws - protocol: ws - description: Local sandbox server - -channels: - /route: - description: Navigation channel for sending requests and receiving live route updates. - publish: - summary: Request navigation from origin to destination - operationId: requestNavigation - message: - $ref: '#/components/messages/NavigationRequest' - subscribe: - summary: Receive live route updates and re-routing events - operationId: receiveRouteUpdate - message: - oneOf: - - $ref: '#/components/messages/RouteUpdate' - - $ref: '#/components/messages/RerouteEvent' - -components: - messages: - NavigationRequest: - name: NavigationRequest - summary: Start navigation between two points - payload: - type: object - required: - - origin - - destination - properties: - requestId: - type: string - description: Client-generated ID to correlate updates with this request - origin: - $ref: '#/components/schemas/Coordinate' - destination: - $ref: '#/components/schemas/Coordinate' - mode: - type: string - enum: [driving, cycling, walking] - default: driving - - RouteUpdate: - name: RouteUpdate - summary: Incremental route progress update - payload: - type: object - properties: - requestId: - type: string - step: - type: integer - description: Current step index in the route - instruction: - type: string - description: Human-readable turn instruction (e.g. "Turn left onto High Street") - coordinates: - type: array - items: - $ref: '#/components/schemas/Coordinate' - distanceRemainingMeters: - type: integer - estimatedArrival: - type: string - format: date-time - - RerouteEvent: - name: RerouteEvent - summary: Emitted when the route is recalculated due to deviation - payload: - type: object - properties: - requestId: - type: string - reason: - type: string - enum: [off_route, traffic, road_closed] - newRoute: - type: array - items: - $ref: '#/components/schemas/Coordinate' - - schemas: - Coordinate: - type: object - required: - - lat - - lng - properties: - lat: - type: number - format: double - description: Latitude in decimal degrees - lng: - type: number - format: double - description: Longitude in decimal degrees diff --git a/portals/api-portal/samples/apis/navigation-ws-v1.0/docs/getting-started.md b/portals/api-portal/samples/apis/navigation-ws-v1.0/docs/getting-started.md deleted file mode 100644 index 58a7b583d..000000000 --- a/portals/api-portal/samples/apis/navigation-ws-v1.0/docs/getting-started.md +++ /dev/null @@ -1,38 +0,0 @@ -# Getting Started - -Connect to the Navigation API using any WebSocket client. - -## Connect - -```javascript -const ws = new WebSocket('wss://navigation.example.com/ws/route', { - headers: { Authorization: 'Bearer ' } -}); -``` - -## Request a route - -```json -{ - "requestId": "req-001", - "origin": { "lat": 51.5074, "lng": -0.1278 }, - "destination": { "lat": 51.5033, "lng": -0.1195 }, - "mode": "walking" -} -``` - -## Receive updates - -The server streams `RouteUpdate` messages as the route progresses: - -```json -{ - "requestId": "req-001", - "step": 1, - "instruction": "Head south on Whitehall", - "distanceRemainingMeters": 1240, - "estimatedArrival": "2025-06-11T14:35:00Z" -} -``` - -If the user goes off-route, a `RerouteEvent` is emitted and the coordinates array is recalculated automatically. diff --git a/portals/api-portal/samples/mcps/everything-mcp-server-v1.0/api.yaml b/portals/api-portal/samples/mcps/everything-mcp-server-v1.0/api.yaml new file mode 100644 index 000000000..27a30ec81 --- /dev/null +++ b/portals/api-portal/samples/mcps/everything-mcp-server-v1.0/api.yaml @@ -0,0 +1,37 @@ +apiVersion: api-portal.api-platform.wso2.com/v1 +kind: MCP + +metadata: + name: everything-mcp-server-v1.0 + +spec: + type: MCP + displayName: Everything MCP Server + version: v1.0 + description: A reference MCP server that exercises the whole protocol — tools, prompts, resources, and sampling. Built for developers testing an MCP client, with a pizza-ordering flow standing in for a real workload. + status: PUBLISHED + + tags: + - mcp + - ai-agent + - reference + - testing + + labels: + - default + + subscriptionPlans: + - Silver + - Bronze + + agentVisibility: VISIBLE + + businessInformation: + businessOwner: API Team + businessOwnerEmail: api-team@example.com + technicalOwner: Platform Team + technicalOwnerEmail: platform-team@example.com + + endpoints: + productionUrl: https://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/mcp-everything-server/v1.0/mcp + sandboxUrl: https://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/mcp-everything-server/v1.0/mcp diff --git a/portals/api-portal/samples/mcps/everything-mcp-server-v1.0/definition.yaml b/portals/api-portal/samples/mcps/everything-mcp-server-v1.0/definition.yaml new file mode 100644 index 000000000..775d7a13b --- /dev/null +++ b/portals/api-portal/samples/mcps/everything-mcp-server-v1.0/definition.yaml @@ -0,0 +1,92 @@ +- type: TOOL + name: echo + description: Echoes back the input. + inputSchema: + type: object + properties: + message: + type: string + description: Message to echo + required: + - message + +- type: TOOL + name: add + description: Adds two numbers. + inputSchema: + type: object + properties: + a: + type: number + description: First number + b: + type: number + description: Second number + required: + - a + - b + +- type: TOOL + name: viewPizzaMenu + description: View the pizza menu. This tool provides a list of available pizzas. + inputSchema: + type: object + properties: {} + +- type: TOOL + name: orderPizza + description: Order a pizza from the menu. This tool allows you to place an order for a pizza. + inputSchema: + type: object + properties: + pizzaType: + type: string + description: Type of pizza to order + quantity: + type: integer + minimum: 1 + description: Number of pizzas to order + customerName: + type: string + description: Name of the customer + deliveryAddress: + type: string + description: Delivery address for the order + creditCardNumber: + type: string + description: Credit card number for payment + required: + - pizzaType + - quantity + - customerName + - deliveryAddress + - creditCardNumber + +- type: RESOURCE + name: test-resource + description: One of 100 numbered test resources. Even-numbered ones return plain text, odd-numbered ones return base64-encoded binary. Supports subscriptions, and subscribed resources auto-update every 5 seconds. + uri: test://static/resource/1 + mimeType: text/plain + +- type: PROMPT + name: simple_prompt + description: A basic prompt that takes no arguments and returns a single message exchange. + +- type: PROMPT + name: complex_prompt + description: Demonstrates argument handling — returns a multi-turn conversation that includes images. + arguments: + - name: temperature + description: Temperature setting + required: true + - name: style + description: Output style preference (e.g. casual, formal, technical, friendly) + required: false + +- type: PROMPT + name: resource_prompt + description: Demonstrates embedding a resource reference directly in prompt messages. + arguments: + - name: resourceId + description: ID of the resource to embed, from 1 to 100 + required: true diff --git a/portals/api-portal/samples/mcps/everything-mcp-server-v1.0/docs/getting-started.md b/portals/api-portal/samples/mcps/everything-mcp-server-v1.0/docs/getting-started.md new file mode 100644 index 000000000..1d23f54a5 --- /dev/null +++ b/portals/api-portal/samples/mcps/everything-mcp-server-v1.0/docs/getting-started.md @@ -0,0 +1,64 @@ +# Getting Started + +The Everything MCP Server implements as much of the Model Context Protocol as possible in one +place — tools, prompts, resources, and sampling. It is not meant to be useful in production; it +exists so you can point an MCP client at something that exercises every part of the protocol. + +## Endpoint + +``` +https://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/mcp-everything-server/v1.0/mcp +``` + +This is a streamable HTTP MCP endpoint — note the trailing `/mcp`, which is part of the address. + +## Tools + +| Tool | Arguments | Does | +|---|---|---| +| `echo` | `message` | Returns the message unchanged | +| `add` | `a`, `b` | Returns the sum | +| `viewPizzaMenu` | — | Returns the available pizzas as JSON | +| `orderPizza` | `pizzaType`, `quantity`, `customerName`, `deliveryAddress`, `creditCardNumber` | Places an order and returns the order details, including an `orderId` | + +`echo` and `add` are the quickest way to confirm a client is connected and passing arguments +correctly. `viewPizzaMenu` and `orderPizza` form a two-step flow: list the menu, then order from +it — a realistic shape for testing how an agent chains one tool's output into the next. + +> The pizza order takes a credit card number because it mimics a real checkout. It is a sample +> server with no payment processing behind it, so use an obviously fake number such as +> `4111 1111 1111 1111` — never a real one. + +## Prompts + +| Prompt | Arguments | Returns | +|---|---|---| +| `simple_prompt` | — | A single message exchange | +| `complex_prompt` | `temperature` (required), `style` | A multi-turn conversation including images | +| `resource_prompt` | `resourceId` (required, 1-100) | A conversation with an embedded resource reference | + +## Resources + +The server exposes 100 numbered test resources under `test://static/resource/{n}`: + +- **Even** numbers return plain text. +- **Odd** numbers return base64-encoded binary. + +They page 10 at a time, support subscriptions, and any resource you subscribe to updates itself +every 5 seconds — which is what makes this server useful for testing a client's resource +subscription handling. + +## Logging + +The server emits a log message at a random level every 15 seconds, so a client that surfaces +`notifications/message` will show activity without you doing anything. + +## Trying it from the portal + +Open the MCP server's page and use the **MCP Playground** to connect and call a tool. `echo` is the +simplest round trip to start with. + +## Source + +The server is published at +[wso2/api-platform-samples/mcp-everything-server](https://github.com/wso2/api-platform-samples/tree/main/mcp-everything-server). diff --git a/portals/api-portal/samples/mcps/travel-assistant-mcp-v1/api.yaml b/portals/api-portal/samples/mcps/travel-assistant-mcp-v1/api.yaml deleted file mode 100644 index 8df7be9aa..000000000 --- a/portals/api-portal/samples/mcps/travel-assistant-mcp-v1/api.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: api-portal.api-platform.wso2.com/v1 -kind: MCP - -metadata: - name: travel-assistant-mcp-v1 - -spec: - type: MCP - displayName: Travel Assistant MCP - version: 1.0.0 - description: MCP server for travel planning tools. - status: PUBLISHED - - tags: - - mcp - - ai-agent - - travel - - labels: - - default - - subscriptionPlans: - - Gold - - Bronze - - agentVisibility: VISIBLE - - businessInformation: - businessOwner: API Team - businessOwnerEmail: api-team@example.com - technicalOwner: Platform Team - technicalOwnerEmail: platform-team@example.com - - endpoints: - productionUrl: https://your-mcp-host.example.com - sandboxUrl: https://your-mcp-host.example.com diff --git a/portals/api-portal/samples/mcps/travel-assistant-mcp-v1/definition.yaml b/portals/api-portal/samples/mcps/travel-assistant-mcp-v1/definition.yaml deleted file mode 100644 index 1422214b2..000000000 --- a/portals/api-portal/samples/mcps/travel-assistant-mcp-v1/definition.yaml +++ /dev/null @@ -1,109 +0,0 @@ -- type: TOOL - name: search_flights - description: Find available flights for a given origin, destination, and travel date. - inputSchema: - type: object - properties: - from: - type: string - description: IATA code of the departure airport (e.g. CMB, SIN, LHR) - to: - type: string - description: IATA code of the arrival airport (e.g. JFK, DXB, SYD) - date: - type: string - format: date - description: Travel date in YYYY-MM-DD format - cabinClass: - type: string - enum: [ECONOMY, BUSINESS, FIRST] - description: Preferred cabin class - required: - - from - - to - - date - -- type: TOOL - name: search_hotels - description: Find available hotels in a city for a given check-in date and duration. - inputSchema: - type: object - properties: - city: - type: string - description: City name or airport code for the hotel search - checkIn: - type: string - format: date - description: Check-in date in YYYY-MM-DD format - nights: - type: integer - minimum: 1 - description: Number of nights to stay - maxPricePerNight: - type: number - description: Maximum acceptable price per night in USD - required: - - city - - checkIn - -- type: TOOL - name: book_trip - description: Book a flight and optionally a hotel for the specified traveller. - inputSchema: - type: object - properties: - flightId: - type: string - description: Flight identifier returned by search_flights - hotelId: - type: string - description: Hotel identifier returned by search_hotels (optional) - passengerName: - type: string - description: Full name of the primary passenger - passengerEmail: - type: string - format: email - description: Contact email for booking confirmation - required: - - flightId - - passengerName - - passengerEmail - -- type: RESOURCE - name: airport-codes - description: Reference list of IATA airport codes and their corresponding city and country names. - uri: resource://travel/airport-codes - mimeType: application/json - -- type: RESOURCE - name: supported-airlines - description: List of airline carriers supported by this MCP server, including their names and IATA codes. - uri: resource://travel/supported-airlines - mimeType: application/json - -- type: PROMPT - name: plan-trip - description: Generate a complete travel itinerary for a destination given traveller preferences. - arguments: - - name: destination - description: The city or country to travel to - required: true - - name: duration - description: Number of days for the trip - required: true - - name: budget - description: Approximate total budget in USD - required: false - - name: interests - description: Comma-separated list of interests (e.g. culture, food, adventure) - required: false - -- type: PROMPT - name: summarize-booking - description: Produce a human-readable summary of a completed booking for the traveller. - arguments: - - name: bookingId - description: The booking reference returned by book_trip - required: true diff --git a/portals/api-portal/samples/mcps/travel-assistant-mcp-v1/docs/getting-started.md b/portals/api-portal/samples/mcps/travel-assistant-mcp-v1/docs/getting-started.md deleted file mode 100644 index 6c4b2f5b6..000000000 --- a/portals/api-portal/samples/mcps/travel-assistant-mcp-v1/docs/getting-started.md +++ /dev/null @@ -1,6 +0,0 @@ -# Getting Started - -1. Subscribe to this MCP. -2. Generate a token from your application. -3. Open **Documentation -> MCP Playground**. -4. Use `Authorization: Bearer `. From 1089d3b7265391b383bc471dcdbb22686f30bd77 Mon Sep 17 00:00:00 2001 From: Piumal Rathnayake Date: Wed, 5 Aug 2026 09:36:34 +0530 Subject: [PATCH 4/4] Address comments --- .../docs/publish-apis/publishing-apis.md | 4 ++-- .../apis/chat-service-api-v1.0/definition.yml | 2 ++ .../chat-service-api-v1.0/docs/getting-started.md | 15 +++++++++------ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/portals/api-portal/docs/publish-apis/publishing-apis.md b/portals/api-portal/docs/publish-apis/publishing-apis.md index 3e323b95e..8942604ab 100644 --- a/portals/api-portal/docs/publish-apis/publishing-apis.md +++ b/portals/api-portal/docs/publish-apis/publishing-apis.md @@ -121,11 +121,11 @@ curl -k -X POST "https://localhost:9543/api-portal/api/v0.9/apis" \ ```bash # MCP server (note: MCP servers are created under /mcp-servers, not /apis). -# An MCP server's contract is its tools schema (schemaDefinition) — it has no apiDefinition. +# An MCP server's contract is its tools schema (definition.yaml) — it has no apiDefinition. curl -k -X POST "https://localhost:9543/api-portal/api/v0.9/mcp-servers" \ -H "Authorization: Bearer $TOKEN" \ -F "metadata=@mcp.yaml" \ - -F "definition=@schemaDefinition.yaml;type=application/yaml" + -F "definition=@definition.yaml;type=application/yaml" ``` | Field | Required | Description | diff --git a/portals/api-portal/samples/apis/chat-service-api-v1.0/definition.yml b/portals/api-portal/samples/apis/chat-service-api-v1.0/definition.yml index 8d60537e7..74b00777b 100644 --- a/portals/api-portal/samples/apis/chat-service-api-v1.0/definition.yml +++ b/portals/api-portal/samples/apis/chat-service-api-v1.0/definition.yml @@ -33,6 +33,8 @@ components: summary: A chat frame, sent in either direction over the same socket. payload: type: object + required: + - type properties: type: type: string diff --git a/portals/api-portal/samples/apis/chat-service-api-v1.0/docs/getting-started.md b/portals/api-portal/samples/apis/chat-service-api-v1.0/docs/getting-started.md index 9d3014c6a..65cbad722 100644 --- a/portals/api-portal/samples/apis/chat-service-api-v1.0/docs/getting-started.md +++ b/portals/api-portal/samples/apis/chat-service-api-v1.0/docs/getting-started.md @@ -15,12 +15,15 @@ paths or methods to choose between. ## Message format -Every frame, in either direction, is a JSON object with two fields: - -| Field | Type | Description | -|---|---|---| -| `type` | string | Frame type, e.g. `connect` or `message` | -| `message` | string | The message content | +Every frame, in either direction, is a JSON object. Only `type` is always present — it +selects the frame's shape, and the service ignores any frame whose type it doesn't +recognise. `message` carries the content of a chat frame, so frames of other types don't +have one. + +| Field | Type | Always present | Description | +|---|---|---|---| +| `type` | string | yes | Frame type, e.g. `connect` or `message` | +| `message` | string | no | The message content, on chat frames | ```json { "type": "message", "message": "Hello from user1" }