1. MCP server
Astravue docs
  • Getting started
    • Introduction
    • Authentication
    • Make your first request
  • General
    • Key concepts
    • Pagination and filtering
    • Errors
    • Dates and times
  • Modules
    • Spaces and projects
    • Tasks
    • Subtasks and checklists
    • Custom fields
    • Comments
    • Time tracking
    • Bulk export
  • MCP server
    • Overview
    • Get started
    • Supported tools
    • Security
    • Build your own client
Guides
API Docs
Guides
API Docs
MCP Server
Astravue MCP on GitHub
  1. MCP server

Integrating your own MCP client

Learn how to connect your own AI tool or application to Astravue MCP using OAuth 2.0.

Overview#

Astravue provides a hosted MCP server that lets any MCP-compatible client interact with your workspace.
If you are building your own AI tool or integration, this guide walks you through the OAuth 2.0 Authorization Code flow with PKCE used by Astravue MCP.
TransportURLNotes
Streamable HTTPhttps://api.astravue.com/mcpOnly supported transport
Key requirements
OAuth 2.0 Authorization Code flow with PKCE
Streamable HTTP transport support
Token refresh handling
Secure credential storage

Prerequisites#

INFO
This guide uses TypeScript / JavaScript examples.
The OAuth 2.0 flow, PKCE implementation, and MCP protocol are language-agnostic — the same concepts apply in any language.
Required libraries (TypeScript / JavaScript)
Python
Go
Java / Spring
Rust
C# / .NET

Specifications#

Model Context Protocol Specification
RFC 6749 — OAuth 2.0 Authorization Framework
RFC 7636 — PKCE
RFC 8414 — Authorization Server Metadata
RFC 9728 — OAuth 2.0 Protected Resource Metadata

1. Discover OAuth metadata#

Before connecting, discover Astravue’s OAuth configuration using the standard discovery process.
1.
Fetch Protected Resource Metadata (RFC 9728)
2.
Fetch Authorization Server Metadata (RFC 8414)
type OAuthMetadata = {
  issuer: string
  authorization_endpoint: string
  token_endpoint: string
  registration_endpoint?: string
}

async function discoverOAuthMetadata(mcpServerUrl: string): Promise<OAuthMetadata> {

  const url = new URL(mcpServerUrl)

  const protectedResourceUrl = new URL(
    "/.well-known/oauth-protected-resource",
    url
  )

  const protectedResourceResponse = await fetch(protectedResourceUrl.toString())

  if (!protectedResourceResponse.ok) {
    throw new Error("Failed to fetch protected resource metadata")
  }

  const protectedResource = await protectedResourceResponse.json()

  const authServerUrl = protectedResource.authorization_servers[0]

  const metadataUrl = new URL(
    "/.well-known/oauth-authorization-server",
    authServerUrl
  )

  const metadataResponse = await fetch(metadataUrl.toString())

  if (!metadataResponse.ok) {
    throw new Error("Failed to fetch authorization server metadata")
  }

  return metadataResponse.json()
}
Astravue discovery endpoints:
https://api.astravue.com/.well-known/oauth-protected-resource
https://api.astravue.com/.well-known/oauth-authorization-server

2. Generate PKCE values#

PKCE protects the OAuth authorization code flow.
import { randomBytes, createHash } from "crypto"

function base64URLEncode(buffer: Buffer): string {
  return buffer
    .toString("base64")
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=/g, "")
}

function generateCodeVerifier(): string {
  return base64URLEncode(randomBytes(32))
}

function generateCodeChallenge(verifier: string): string {

  const hash = createHash("sha256")
    .update(verifier)
    .digest()

  return base64URLEncode(hash)
}

const codeVerifier = generateCodeVerifier()

const codeChallenge = generateCodeChallenge(codeVerifier)
WARNING
Never send the codeVerifier until the token exchange step.
Store it securely in memory or encrypted session storage.

3. Register your client#

Astravue MCP supports RFC 7591 dynamic client registration.
async function registerClient(metadata: OAuthMetadata, redirectUri: string) {

  const registration = {
    client_name: "Your MCP Client",
    redirect_uris: [redirectUri]
  }

  const response = await fetch(metadata.registration_endpoint!, {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(registration)
  })

  if (!response.ok) {
    throw new Error("Client registration failed")
  }

  return response.json()
}

4. Build the sign-in URL#

function buildAuthorizationUrl(
  metadata: OAuthMetadata,
  clientId: string,
  redirectUri: string,
  codeChallenge: string,
  state: string
) {

  const params = new URLSearchParams({
    response_type: "code",
    client_id: clientId,
    redirect_uri: redirectUri,
    scope: "openid profile email",
    state: state,
    code_challenge: codeChallenge,
    code_challenge_method: "S256",
    resource: mcpServerUrl
  })

  return `${metadata.authorization_endpoint}?${params}`
}
Redirect your user to this URL to begin authentication.

5. Handle the callback#

function parseCallback(url: string) {

  const params = new URLSearchParams(new URL(url).search)

  return {
    code: params.get("code"),
    state: params.get("state"),
    error: params.get("error")
  }
}
Always validate the state parameter before continuing.

6. Exchange the code#

async function exchangeCodeForTokens(
  code: string,
  codeVerifier: string,
  metadata: OAuthMetadata,
  clientId: string,
  redirectUri: string
) {

  const params = new URLSearchParams({
    grant_type: "authorization_code",
    code,
    client_id: clientId,
    redirect_uri: redirectUri,
    code_verifier: codeVerifier,
    resource: "https://api.astravue.com/mcp"
  })

  const response = await fetch(metadata.token_endpoint, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded"
    },
    body: params.toString()
  })

  if (!response.ok) {
    throw new Error("Token exchange failed")
  }

  return response.json()
}
The resource parameter ensures the access token is issued for the Astravue MCP server (sets the aud claim).
WARNING
Token storage guidelines
Web apps — store tokens server-side only
Desktop apps — use OS keychain
Mobile apps — use secure keystore APIs
Always encrypt tokens at rest

7. Connect to the server#

Astravue MCP uses Streamable HTTP transport only.
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"

async function createMcpClient(accessToken: string): Promise<Client> {

  const client = new Client(
    { name: "your-mcp-client", version: "1.0.0" },
    { capabilities: { roots: {}, sampling: {} } }
  )

  const transport = new StreamableHTTPClientTransport(
    new URL("https://api.astravue.com/mcp"),
    {
      headers: {
        Authorization: `Bearer ${accessToken}`,
        "User-Agent": "YourApp-MCP-Client/1.0"
      }
    }
  )

  await client.connect(transport)

  return client
}

8. Refresh tokens#

Access tokens expire after 30 minutes.
async function refreshAccessToken(
  refreshToken: string,
  metadata: OAuthMetadata,
  clientId: string
) {

  const params = new URLSearchParams({
    grant_type: "refresh_token",
    refresh_token: refreshToken,
    client_id: clientId
  })

  const response = await fetch(metadata.token_endpoint, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded"
    },
    body: params.toString()
  })

  if (!response.ok) {
    throw new Error("Token refresh failed")
  }

  return response.json()
}
INFO
Astravue uses refresh token rotation.
Each refresh request returns a new refresh token and invalidates the previous one.

Token lifetimes#

TokenLifetimePurpose
Access token30 minAuthorizes MCP tool calls
Refresh token24 hoursIssues new access tokens

Security checklist#

Always use HTTPS
Validate OAuth state parameter
Encrypt stored tokens
Always use PKCE S256
Refresh tokens before expiry
Handle invalid_grant gracefully
Log OAuth operations for auditing

Troubleshooting#

OAuth discovery fails
Invalid state parameter
invalid audience
Token refresh fails

Additional resources#

Model Context Protocol Specification
MCP TypeScript SDK
RFC 7636 — PKCE
RFC 9728— OAuth 2.0 Protected Resource Metadata
Modified at 2026-11-10 17:10:34
Previous
Security
Built with