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.
https://aicoach.pw Authentication
Public read endpoints require no authentication. Endpoints that create or modify resources require a Bearer token.
Authorization: Bearer mcp_xxxxx Three ways to get an API key
Generate keys at /account/api-keys. Best for personal scripts and testing.
Auto-register your app, then authenticate users via PKCE flow. No secrets needed. Best for client apps.
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.
/api/v1/mcp/servers List and search published MCP servers with cursor-based pagination.
| Param | Type | Description |
|---|---|---|
| search | string | Substring match on display name |
| cursor | string | Base64 pagination cursor from previous response |
| limit | number | 1–100 (default 50) |
| updated_since | string | ISO 8601 datetime for incremental sync |
| version | string | Set to latest to return only latest versions |
curl "https://aicoach.pw/api/v1/mcp/servers?search=postgres&limit=10" {
"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 }
} /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.
{
"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"
} | Field | Required | Description |
|---|---|---|
| title | Yes | Display name (or displayName) |
| description | Yes | Short description (or descriptionShort) |
| name | No | Reverse-DNS server name (auto-generated if omitted) |
| version | No | Semver string (default: 1.0.0) |
| transport | No | stdio | sse | streamable-http (default: stdio) |
| external_id | No | Your app's internal reference ID for correlation |
| repository | No | Object with url and source |
| command | No | CLI command (e.g. npx, uvx) |
| sseUrl | No | SSE endpoint URL (for sse/streamable-http transport) |
| websiteUrl | No | Project homepage |
{
"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"
}
}
} {
"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": { ... }
}
} /api/v1/mcp/by-slug/:slug Get full details for a single MCP server by its URL slug.
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.
/api/v1/mcp/servers/:serverName/versions List all versions of a server, ordered by publish date (newest first).
| Param | Type | Description |
|---|---|---|
| include_deleted | boolean | Include soft-deleted versions |
curl "https://aicoach.pw/api/v1/mcp/servers/com.example%2Fmy-server/versions" /api/v1/mcp/servers/:serverName/versions/:version Get, update, or delete a specific version. Use latest as the version to target the most recent.
{
"displayName": "Updated Server Name",
"descriptionShort": "Updated description",
"version": "1.1.0",
"transport": "sse",
"sseUrl": "https://example.com/sse",
"external_id": "your-internal-ref-123"
} deleted) Auth Required /api/v1/mcp/servers/:serverName/status Auth Required Update status across all versions of a server at once.
{
"status": "deprecated",
"statusMessage": "Use v2 instead"
} | Field | Values | Description |
|---|---|---|
| status | active | deprecated | deleted | New lifecycle status |
| statusMessage | string | Optional message (e.g. migration instructions) |
/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.
/api/v1/skills/list List and search skills with optional filtering.
| Param | Type | Description |
|---|---|---|
| q | string | Search query |
| category | string | Filter by category slug |
| limit | number | Results per page (default 50) |
| offset | number | Pagination offset |
curl "https://aicoach.pw/api/v1/skills/list?q=commit&limit=5" /api/v1/skills/categories List all skill categories with slugs and skill counts.
/api/v1/skills/:slug Get full skill details including install command and SKILL.md URL.
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 } ──────────┤ │ Register — Call /api/v1/oauth/register with your app name and redirect URIs. Returns a client_id. Idempotent: same name + URIs = same client_id.
Login — Ensure the user is logged in via GitHub. Redirect to /api/auth/login if no session exists.
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).
Exchange — POST code + code_verifier to /api/v1/oauth/token. No client_secret needed. Returns an API key.
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.
| Concern | How it's enforced |
|---|---|
| Server ownership | ownerGithubId set from the authenticated user, not the client app |
| API key isolation | Each OAuth authorization produces a unique key bound to one user |
| Cross-tenant protection | Publish returns 409 if name/slug collides with a different owner |
| Correlation | external_id field lets your app link back to its own tenant records |
| Revocation | Deactivating a client app revokes all API keys issued through it |
OAuth Endpoints
/api/v1/oauth/register Public Dynamic client registration (RFC 7591). No authentication required. Returns a PKCE-only client_id — no client_secret is issued.
{
"client_name": "My App",
"redirect_uris": ["https://myapp.com/callback"],
"client_uri": "https://myapp.com",
"logo_uri": "https://myapp.com/logo.png"
} | Field | Required | Description |
|---|---|---|
| client_name | Yes | Your application name |
| redirect_uris | Yes | Array of callback URLs (HTTPS required, except localhost) |
| client_uri | No | Your app's homepage |
| logo_uri | No | Your app's logo URL |
{
"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"]
} /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.
{
"client_id": "ocp_a1b2c3d4e5f6g7h8",
"redirect_uri": "https://myapp.com/callback",
"code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
"code_challenge_method": "S256",
"state": "random-csrf-state"
} | Field | Required | Description |
|---|---|---|
| client_id | Yes | From registration |
| redirect_uri | Yes | Must exactly match a registered URI |
| code_challenge | PKCE clients | BASE64URL(SHA256(code_verifier)) |
| code_challenge_method | No | Only S256 is supported (default) |
| state | No | Opaque CSRF value, echoed in 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.
/api/v1/oauth/token Public Exchange an authorization code for an API key. CORS-enabled.
{
"grant_type": "authorization_code",
"code": "a1b2c3d4e5f6...",
"client_id": "ocp_a1b2c3d4e5f6g7h8",
"redirect_uri": "https://myapp.com/callback",
"code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
} | Field | Required | Description |
|---|---|---|
| code | Yes | Authorization code from previous step |
| client_id | Yes | Your registered client ID |
| redirect_uri | Yes | Must match the URI used at authorize |
| code_verifier | PKCE | Original random string (43–128 chars) |
| client_secret | Confidential | For non-PKCE clients only |
| grant_type | No | Must be authorization_code if provided |
{
"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
Register at /account/apps to get
client_id+client_secret. - 2
Redirect user to authorize:
https://aicoach.pw/connect?client_id=YOUR_ID&redirect_uri=YOUR_CALLBACK&state=RANDOM - 3
User approves. Your callback receives a
codeparameter. - 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.
/api/publisher/mcp-servers Auth Required List all MCP servers owned by the authenticated user.
curl https://aicoach.pw/api/publisher/mcp-servers \
-H "Authorization: Bearer mcp_xxxxx" /api/publisher/mcp-servers Auth Required Create a new MCP server. Starts as draft (approved=false).
{
"serverName": "com.example.my-server",
"slug": "my-server",
"version": "1.0.0",
"displayName": "My Awesome Server",
"descriptionShort": "Brief description (max 160 chars)",
"transport": "stdio"
} /api/publisher/mcp-servers/:id Auth Required Update server metadata. Send approved: true to publish.
/api/publisher/mcp-servers/:id Auth Required Delete an MCP server permanently.
Errors & Rate Limits
HTTP Status Codes
Rate Limits
| Endpoint Type | Limit |
|---|---|
| Public read endpoints | 100 requests/minute |
| Create / publish | 10 requests/hour |
| Update / status | 60 requests/hour |
| OAuth register | 20 requests/hour per IP |