API Reference

AI Coach API

Browse MCP servers, publish to the registry, query skills, and integrate via OAuth. All responses are JSON. Public endpoints are CORS-enabled.

Base URL
https://aicoach.pw

Authentication

Public read endpoints require no authentication. Endpoints that create or modify resources require a Bearer token.

Header Format
Authorization: Bearer mcp_xxxxx

Three ways to get an API key

Manual

Generate keys at /account/api-keys. Best for personal scripts and testing.

PKCE OAuth Recommended

Auto-register your app, then authenticate users via PKCE flow. No secrets needed. Best for client apps.

Confidential OAuth

Register at /account/apps to get client_secret. Best for backend services. See details.

MCP Registry API

Discover, publish, and manage MCP servers. Public read endpoints are CORS-enabled.

GET /api/v1/mcp/servers

List and search published MCP servers with cursor-based pagination.

ParamTypeDescription
searchstringSubstring match on display name
cursorstringBase64 pagination cursor from previous response
limitnumber1–100 (default 50)
updated_sincestringISO 8601 datetime for incremental sync
versionstringSet to latest to return only latest versions
Example
curl "https://aicoach.pw/api/v1/mcp/servers?search=postgres&limit=10"
Response
{
  "servers": [
    {
      "server": {
        "name": "io.github.user/postgres-mcp",
        "title": "PostgreSQL MCP",
        "description": "Query PostgreSQL databases via MCP",
        "version": "1.2.0"
      },
      "_meta": {
        "io.modelcontextprotocol.registry/official": {
          "status": "active",
          "publishedAt": "2025-06-01T00:00:00Z",
          "updatedAt": "2025-07-15T12:00:00Z",
          "isLatest": true
        },
        "aicoach.pw": {
          "id": "uuid-...",
          "slug": "postgres-mcp",
          "external_id": null,
          "transport": "stdio",
          "approved": true,
          "visibility": "public",
          "detailUrl": "https://aicoach.pw/mcp/postgres-mcp"
        }
      }
    }
  ],
  "metadata": { "count": 1, "nextCursor": null }
}
POST /api/v1/mcp/publish Auth Required

Publish a new MCP server or update an existing one. Upsert behavior: if a server with the same name or slug already exists for your account, it is updated instead of duplicated.

Request Body
{
  "name": "com.example/my-server",
  "title": "My MCP Server",
  "description": "Brief description of the server",
  "version": "1.0.0",
  "transport": "stdio",
  "external_id": "your-internal-ref-123",
  "repository": {
    "url": "https://github.com/user/repo",
    "source": "github"
  },
  "command": "npx",
  "websiteUrl": "https://example.com"
}
FieldRequiredDescription
titleYesDisplay name (or displayName)
descriptionYesShort description (or descriptionShort)
nameNoReverse-DNS server name (auto-generated if omitted)
versionNoSemver string (default: 1.0.0)
transportNostdio | sse | streamable-http (default: stdio)
external_idNoYour app's internal reference ID for correlation
repositoryNoObject with url and source
commandNoCLI command (e.g. npx, uvx)
sseUrlNoSSE endpoint URL (for sse/streamable-http transport)
websiteUrlNoProject homepage
Response — 201 Created (new) or 200 OK (updated)
{
  "server": {
    "name": "com.example/my-server",
    "title": "My MCP Server",
    "description": "Brief description of the server",
    "version": "1.0.0"
  },
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "status": "active",
      "publishedAt": "2026-04-02T00:00:00Z",
      "updatedAt": "2026-04-02T00:00:00Z",
      "isLatest": true
    },
    "aicoach.pw": {
      "id": "uuid-...",
      "slug": "com.example--my-server",
      "external_id": "your-internal-ref-123",
      "transport": "stdio",
      "approved": false,
      "visibility": "private",
      "detailUrl": "https://aicoach.pw/mcp/com.example--my-server"
    }
  }
}
Response — 409 Conflict (name/slug owned by another user)
{
  "error": "Duplicate name or slug",
  "message": "A server with this name or slug already exists (slug: com.example--my-server)",
  "existing": {
    "server": { "name": "...", "title": "...", ... },
    "_meta": { ... }
  }
}
GET /api/v1/mcp/by-slug/:slug

Get full details for a single MCP server by its URL slug.

Example
curl "https://aicoach.pw/api/v1/mcp/by-slug/filesystem"

Version & Status Management

Manage server versions and lifecycle status. All endpoints require authentication and ownership of the server.

GET /api/v1/mcp/servers/:serverName/versions

List all versions of a server, ordered by publish date (newest first).

ParamTypeDescription
include_deletedbooleanInclude soft-deleted versions
Example
curl "https://aicoach.pw/api/v1/mcp/servers/com.example%2Fmy-server/versions"
GET PUT DELETE /api/v1/mcp/servers/:serverName/versions/:version

Get, update, or delete a specific version. Use latest as the version to target the most recent.

PUT Update version metadata Auth Required
Request Body (all fields optional)
{
  "displayName": "Updated Server Name",
  "descriptionShort": "Updated description",
  "version": "1.1.0",
  "transport": "sse",
  "sseUrl": "https://example.com/sse",
  "external_id": "your-internal-ref-123"
}
DELETE Soft-delete a version (sets status to deleted) Auth Required
PATCH /api/v1/mcp/servers/:serverName/status Auth Required

Update status across all versions of a server at once.

Request Body
{
  "status": "deprecated",
  "statusMessage": "Use v2 instead"
}
FieldValuesDescription
statusactive | deprecated | deletedNew lifecycle status
statusMessagestringOptional message (e.g. migration instructions)
PATCH /api/v1/mcp/servers/:serverName/versions/:version/status Auth Required

Update status for a single version. Same request body as above. Returns 400 if the status is already set to the requested value.

Skills API

Public, CORS-enabled endpoints for discovering AI coaching skills.

GET /api/v1/skills/list

List and search skills with optional filtering.

ParamTypeDescription
qstringSearch query
categorystringFilter by category slug
limitnumberResults per page (default 50)
offsetnumberPagination offset
Example
curl "https://aicoach.pw/api/v1/skills/list?q=commit&limit=5"
GET /api/v1/skills/categories

List all skill categories with slugs and skill counts.

GET /api/v1/skills/:slug

Get full skill details including install command and SKILL.md URL.

Example
curl "https://aicoach.pw/api/v1/skills/commit"

OAuth & PKCE

Integrate your application with AI Coach using OAuth 2.0 with PKCE (RFC 7636). Apps auto-register at runtime — no manual setup needed. Each user gets their own API key; server ownership is isolated per user.

PKCE Auto-Registration Flow

Your App                          AI Coach                GitHub
  │                                  │                       │
  ├─① POST /api/v1/oauth/register──►│                       │
  │  { client_name, redirect_uris } │                       │
  │◄── { client_id } ──────────────┤                       │
  │                                  │                       │
  ├─② Redirect user ────────────────┼──► GitHub OAuth ──►  │
  │                                  │  ◄── session cookie ──┤
  │                                  │                       │
  ├─③ POST /api/v1/oauth/authorize─►│                       │
  │  { client_id, code_challenge }  │                       │
  │◄── { code } ───────────────────┤                       │
  │                                  │                       │
  ├─④ POST /api/v1/oauth/token ───►│                       │
  │  { code, code_verifier }        │                       │
  │◄── { api_key } ────────────────┤                       │
  │                                  │                       │
  ├─⑤ POST /api/v1/mcp/publish ───►│                       │
  │  Authorization: Bearer mcp_xxx  │                       │
  │◄── { server, _meta } ──────────┤                       │
1

Register — Call /api/v1/oauth/register with your app name and redirect URIs. Returns a client_id. Idempotent: same name + URIs = same client_id.

2

Login — Ensure the user is logged in via GitHub. Redirect to /api/auth/login if no session exists.

3

Authorize — Generate a PKCE pair (code_verifier + code_challenge = BASE64URL(SHA256(verifier))). POST to /api/v1/oauth/authorize with the challenge. Returns a single-use authorization code (5 min TTL).

4

Exchange — POST code + code_verifier to /api/v1/oauth/token. No client_secret needed. Returns an API key.

5

Use — Use the API key as Authorization: Bearer mcp_xxx on any authenticated endpoint.

Multi-Tenant Isolation

One client_id per app, many users. Each user authenticates separately and gets their own API key. Server ownership is enforced by the user's GitHub identity — User A cannot modify User B's servers.

ConcernHow it's enforced
Server ownershipownerGithubId set from the authenticated user, not the client app
API key isolationEach OAuth authorization produces a unique key bound to one user
Cross-tenant protectionPublish returns 409 if name/slug collides with a different owner
Correlationexternal_id field lets your app link back to its own tenant records
RevocationDeactivating a client app revokes all API keys issued through it

OAuth Endpoints

POST /api/v1/oauth/register Public

Dynamic client registration (RFC 7591). No authentication required. Returns a PKCE-only client_id — no client_secret is issued.

Request Body
{
  "client_name": "My App",
  "redirect_uris": ["https://myapp.com/callback"],
  "client_uri": "https://myapp.com",
  "logo_uri": "https://myapp.com/logo.png"
}
FieldRequiredDescription
client_nameYesYour application name
redirect_urisYesArray of callback URLs (HTTPS required, except localhost)
client_uriNoYour app's homepage
logo_uriNoYour app's logo URL
Response — 201 Created (new) or 200 OK (existing match)
{
  "client_id": "ocp_a1b2c3d4e5f6g7h8",
  "client_name": "My App",
  "redirect_uris": ["https://myapp.com/callback"],
  "client_id_issued_at": 1743465600,
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code"],
  "response_types": ["code"]
}
POST /api/v1/oauth/authorize Session Required

Generate an authorization code. User must be logged in via GitHub session. For PKCE clients, code_challenge is required.

Request Body
{
  "client_id": "ocp_a1b2c3d4e5f6g7h8",
  "redirect_uri": "https://myapp.com/callback",
  "code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
  "code_challenge_method": "S256",
  "state": "random-csrf-state"
}
FieldRequiredDescription
client_idYesFrom registration
redirect_uriYesMust exactly match a registered URI
code_challengePKCE clientsBASE64URL(SHA256(code_verifier))
code_challenge_methodNoOnly S256 is supported (default)
stateNoOpaque CSRF value, echoed in response
Response
{
  "code": "a1b2c3d4e5f6...",
  "redirect_uri": "https://myapp.com/callback",
  "state": "random-csrf-state"
}

Authorization codes expire in 5 minutes and are single-use. Returns 401 with login_url if the user has no active session.

POST /api/v1/oauth/token Public

Exchange an authorization code for an API key. CORS-enabled.

Request Body (PKCE)
{
  "grant_type": "authorization_code",
  "code": "a1b2c3d4e5f6...",
  "client_id": "ocp_a1b2c3d4e5f6g7h8",
  "redirect_uri": "https://myapp.com/callback",
  "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}
FieldRequiredDescription
codeYesAuthorization code from previous step
client_idYesYour registered client ID
redirect_uriYesMust match the URI used at authorize
code_verifierPKCEOriginal random string (43–128 chars)
client_secretConfidentialFor non-PKCE clients only
grant_typeNoMust be authorization_code if provided
Response
{
  "api_key": "mcp_a1b2c3d4...",
  "key_prefix": "mcp_a1b2c3d4",
  "key_id": "uuid-...",
  "token_type": "bearer"
}

Use the returned api_key as Authorization: Bearer mcp_xxx on authenticated endpoints.

Confidential Client Flow

For backend services that can securely store a client_secret. Register manually at /account/apps (requires publisher status).

  1. 1

    Register at /account/apps to get client_id + client_secret.

  2. 2

    Redirect user to authorize:

    https://aicoach.pw/connect?client_id=YOUR_ID&redirect_uri=YOUR_CALLBACK&state=RANDOM
  3. 3

    User approves. Your callback receives a code parameter.

  4. 4

    Exchange code for API key (backend-to-backend):

    {
      "code": "a1b2c3d4e5f6...",
      "client_id": "ocp_xxxxx",
      "client_secret": "ocs_xxxxx",
      "redirect_uri": "https://myapp.com/callback"
    }

Both flows use the same /api/v1/oauth/token endpoint. Provide client_secret instead of code_verifier.

Publisher API

Manage your MCP servers via the publisher dashboard endpoints. Requires authentication and publisher status.

GET /api/publisher/mcp-servers Auth Required

List all MCP servers owned by the authenticated user.

Example
curl https://aicoach.pw/api/publisher/mcp-servers \
  -H "Authorization: Bearer mcp_xxxxx"
POST /api/publisher/mcp-servers Auth Required

Create a new MCP server. Starts as draft (approved=false).

Required Fields
{
  "serverName": "com.example.my-server",
  "slug": "my-server",
  "version": "1.0.0",
  "displayName": "My Awesome Server",
  "descriptionShort": "Brief description (max 160 chars)",
  "transport": "stdio"
}
PATCH /api/publisher/mcp-servers/:id Auth Required

Update server metadata. Send approved: true to publish.

DELETE /api/publisher/mcp-servers/:id Auth Required

Delete an MCP server permanently.

Errors & Rate Limits

HTTP Status Codes

200 OK — Request succeeded (update or read)
201 Created — New resource created (publish, register)
400 Bad Request — Invalid parameters, JSON body, or PKCE challenge
401 Unauthorized — Invalid or missing API key / session
403 Forbidden — Insufficient permissions or quota exceeded
404 Not Found — Resource doesn't exist
409 Conflict — Duplicate server name/slug (includes existing server in response)
500 Internal Server Error

Rate Limits

Endpoint TypeLimit
Public read endpoints100 requests/minute
Create / publish10 requests/hour
Update / status60 requests/hour
OAuth register20 requests/hour per IP