API/Tools API

Tools API

Discover and execute organization tools from one unified registry.

Every organization tool is declared once in convex/tools/definitions/ and exposed through three surfaces: this REST API, the MCP server, and the pnpm tools CLI. Adding a tool to the registry publishes it everywhere at once.

Endpoints

GET  /api/v1/tools
GET  /api/v1/tools/:name
POST /api/v1/tools/:name
EndpointDescription
GET /api/v1/toolsList every tool with its JSON Schema
GET /api/v1/tools/:nameJSON Schema of a single tool
POST /api/v1/tools/:nameExecute a tool with a JSON body as its input

Authentication

Create an organization API key from the organization settings page:

/orgs/{orgSlug}/settings/api-keys

Send it with x-api-key or as a bearer token:

x-api-key: YOUR_API_KEY
Authorization: Bearer YOUR_API_KEY

The key resolves the organization, so no organization id is ever passed in a tool input.

An OAuth access token issued to an MCP client is sent the same way, as a bearer token. API keys are recognized by their nsk_ prefix, everything else is verified as an OAuth token, so both credentials reach the same handlers.

Available tools

ToolCategoryAccessInputReturns
get_organizationorganizationreadnoneOrganization id, name, slug
list_membersmembersread{ cursor?, limit? }{ members, count, nextCursor, hasMore }
get_membermembersread{ memberId }One member
get_subscriptionbillingreadnonePlan, status, seats and plan limits

Discovery response

GET /api/v1/tools returns a JSON Schema per tool, ready to feed an LLM or a client generator:

{
  "total": 4,
  "tools": [
    {
      "name": "get_member",
      "description": "Get a single member of the organization by member id.",
      "category": "members",
      "access": "read",
      "inputSchema": {
        "type": "object",
        "properties": {
          "memberId": { "type": "string", "minLength": 1 }
        },
        "required": ["memberId"]
      },
      "endpoint": "/api/v1/tools/get_member",
      "method": "POST",
      "route": { "method": "GET", "path": "/api/v1/members/:memberId" }
    }
  ]
}

route is the tool's REST alias, or null when it only answers on /api/v1/tools/<name>. Both paths run the same tool; the alias returns the payload without the data envelope. See Unified Tools for how to declare one.

Execution response

Successful executions return the tool payload under data:

{
  "data": {
    "members": [{ "id": "mem_123", "role": "owner" }],
    "count": 1,
    "nextCursor": null,
    "hasMore": false
  }
}

CLI

export NOWSTACK_API_KEY="YOUR_API_KEY"
pnpm tools list
pnpm tools run get_member '{"memberId":"mem_123"}'

MCP

The MCP server mounts at /api/mcp and serves the same registry. Authenticate with the same organization API key:

{
  "mcpServers": {
    "nowstack": {
      "url": "https://nowstack.melvynx.dev/api/mcp",
      "headers": { "Authorization": "Bearer YOUR_API_KEY" }
    }
  }
}

MCP OAuth

Clients that cannot store an API key (ChatGPT connectors, Claude, MCP Inspector) sign in with OAuth instead. Point the client at /api/mcp with no credentials and it discovers everything it needs:

  1. The unauthenticated request answers 401 with a WWW-Authenticate header pointing at /.well-known/oauth-protected-resource/api/mcp.
  2. That document names the authorization server, /api/auth, whose metadata is served from /.well-known/oauth-authorization-server.
  3. The client registers itself dynamically, then sends the user through sign in, organization selection (/auth/oauth/select-organization) and consent (/auth/oauth/consent).
  4. The issued access token is a JWT carrying the chosen organization_id, so it is scoped to exactly one organization, exactly like an API key.
ScopeGrants
nowstack.readEvery read tool and the discovery endpoints
nowstack.writeRequired on top of nowstack.read for write tools

Only owners and admins can authorize a connection. A token missing nowstack.write gets a 403 when it calls a write tool.

Access tokens are short-lived. When one expires, the client starts the authorization-code flow again so the current organization role and consent are checked before a new token is issued.

Errors

StatusDescription
400Invalid JSON body or input that fails the tool schema
401Missing, invalid, or expired key or access token
403Valid token without the scope the tool requires
404Unknown tool, or a resource the tool could not find
413Request body larger than 256 KB
500Internal server error
Get Organization Member APIAuth Components