Authentication Context
Access user data and authenticated clients seamlessly in your MCP tools, prompts, and resources
The Authentication Context is the core mechanism that provides your MCP tools, prompts, and resources with seamless access to authenticated user data and services. No manual token management, no complex OAuth flows—just direct access to user accounts.
How It Works
When a user connects their accounts to the Osiris Hub and creates scoped connections to your MCP, the authentication context automatically provides:
- ✅ Authenticated service clients (Gmail, GitHub, Discord, etc.)
- ✅ Access tokens with proper scopes and refresh handling
- ✅ User context (session ID, deployment ID, organization info)
- ✅ Database connections with user-specific data access
Basic Usage
The getAuthContext()
Function
Every tool, prompt, and resource can access authentication context using the getAuthContext()
function:
import { getAuthContext } from '@osiris-ai/sdk';
import { createSuccessResponse, createErrorResponse } from '../utils/types.js';
import { z } from 'zod';
// Tool registration in your MCP server configuration
server.tool(
'my_authenticated_tool',
'A tool that accesses user data',
{
query: z.string()
},
async ({ query }) => {
try {
// Get authentication context
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
// Now you have access to:
// - token.access_token: OAuth access token
// - context.sessionId: User session identifier
// - context.deploymentId: MCP deployment identifier
// - context.organizationId: User's organization
return createSuccessResponse("Authentication context retrieved successfully", {
sessionId: context.sessionId,
deploymentId: context.deploymentId
});
} catch (error) {
return createErrorResponse(error);
}
}
);
Service Client Integration
Google Services (Gmail, Calendar, Drive)
import { createHubGmailClient } from '@osiris-ai/google-sdk';
import { getAuthContext } from '@osiris-ai/sdk';
import { createSuccessResponse, createErrorResponse } from '../utils/types.js';
import { z } from 'zod';
// Register the send email tool
server.tool(
'send_email',
'Send an email using Gmail',
{
to: z.string().email(),
subject: z.string(),
body: z.string()
},
async ({ to, subject, body }) => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
// Create authenticated Gmail client
const gmail = await createHubGmailClient({
hubBaseUrl: "https://api.osirislabs.xyz/v1",
token: token,
context: context
});
// Send email with full authentication
const emailContent = [
`To: ${to}`,
`Subject: ${subject}`,
'MIME-Version: 1.0',
'Content-Type: text/plain; charset=UTF-8',
'',
body
];
const raw = Buffer.from(emailContent.join('\r\n'))
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_');
const result = await gmail.users.messages.send({
userId: 'me',
requestBody: { raw }
});
return createSuccessResponse(
`✅ Email sent successfully! Message ID: ${result.data.id}`,
{ messageId: result.data.id, threadId: result.data.threadId }
);
} catch (error) {
return createErrorResponse(error);
}
}
);
GitHub Integration
import { createHubGithubClient } from '@osiris-ai/github-sdk';
import { getAuthContext } from '@osiris-ai/sdk';
import { createSuccessResponse, createErrorResponse } from '../utils/types.js';
import { z } from 'zod';
// Register the create issue tool
server.tool(
'create_github_issue',
'Create a GitHub issue',
{
owner: z.string(),
repo: z.string(),
title: z.string(),
body: z.string()
},
async ({ owner, repo, title, body }) => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
// Create authenticated GitHub client
const github = await createHubGithubClient({
hubBaseUrl: "https://api.osirislabs.xyz/v1",
token: token,
context: context
});
// Create issue with user's GitHub account
const issue = await github.rest.issues.create({
owner,
repo,
title,
body
});
return createSuccessResponse(
`✅ Issue created: ${issue.data.html_url}`,
{
issueNumber: issue.data.number,
issueUrl: issue.data.html_url,
issueId: issue.data.id
}
);
} catch (error) {
return createErrorResponse(error);
}
}
);
Wallet Integration (Web3)
import { EVMWalletClient } from '@osiris-ai/web3-evm-sdk';
import { getAuthContext } from '@osiris-ai/sdk';
import { createSuccessResponse, createErrorResponse } from '../utils/types.js';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
export class Web3MCP {
private hubBaseUrl: string;
private walletToSession: Record<string, string> = {};
constructor(hubBaseUrl: string) {
this.hubBaseUrl = hubBaseUrl;
}
public configureServer(server: McpServer): void {
// Get available wallet addresses
server.tool(
'get_user_addresses',
'Get user wallet addresses - call this first to see available wallets',
{},
async () => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
const walletClient = new EVMWalletClient(
this.hubBaseUrl,
token.access_token,
context.deploymentId
);
const walletRecords = await walletClient.getWalletRecords();
if (walletRecords.length === 0) {
return createErrorResponse("No wallet records found");
}
const addresses = walletRecords.map((record) =>
record.accounts.addresses.map((address) => ({
address: address.address,
chains: address.chains,
walletRecordId: record.id
}))
).flat();
return createSuccessResponse(
"Available wallet addresses retrieved",
{ addresses }
);
} catch (error) {
return createErrorResponse(error);
}
}
);
// Choose wallet for session
server.tool(
'choose_wallet',
'Choose a wallet address for this session - required before performing wallet operations',
{
address: z.string()
},
async ({ address }) => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
const walletClient = new EVMWalletClient(
this.hubBaseUrl,
token.access_token,
context.deploymentId
);
const walletRecords = await walletClient.getWalletRecords();
const walletRecord = walletRecords.find((record) =>
record.accounts.addresses.some((addr) =>
addr.address.toLowerCase() === address.toLowerCase()
)
);
if (!walletRecord) {
return createErrorResponse("Wallet address not found in user records");
}
// Store wallet selection for this session
this.walletToSession[context.sessionId] = address;
return createSuccessResponse(
"Wallet selected for session",
{
address,
walletRecordId: walletRecord.id,
sessionId: context.sessionId
}
);
} catch (error) {
return createErrorResponse(error);
}
}
);
// Get wallet balance
server.tool(
'get_wallet_balance',
'Get wallet balance for the selected wallet address',
{
tokenAddress: z.string().optional()
},
async ({ tokenAddress }) => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
// Check if wallet is selected for this session
const selectedWallet = this.walletToSession[context.sessionId];
if (!selectedWallet) {
return createErrorResponse(
"No wallet selected. Please call 'choose_wallet' first with a wallet address."
);
}
// Your wallet balance logic here...
return createSuccessResponse(
"Wallet balance retrieved",
{
address: selectedWallet,
balance: "0.0" // Implement actual balance logic
}
);
} catch (error) {
return createErrorResponse(error);
}
}
);
}
}
Important: Wallet Session Management
For Web3/wallet operations, users must first choose a wallet address for their session:
- Get Available Wallets: Call
get_user_addresses
to see all connected wallet addresses - Choose Wallet: Call
choose_wallet
with the desired address to set it for the current session - Perform Operations: Now you can call other wallet tools like
get_wallet_balance
,swap
, etc.
This pattern is required because users can have multiple wallet addresses, and the MCP needs to know which one to use for operations.
Database Access
The authentication context provides access to user-specific database storage through the Hub's action API:
import { getAuthContext } from '@osiris-ai/sdk';
import { createSuccessResponse, createErrorResponse } from '../utils/types.js';
import { z } from 'zod';
// Register tool in your MCP configure function
export function configureServer(server: McpServer) {
server.tool(
'save_preference',
'Save user preference to database',
{
key: z.string(),
value: z.string()
},
async ({ key, value }) => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
// Access database through Hub action API
const databaseUrl = `https://api.osirislabs.xyz/v1/hub/action/${context.deploymentId}/postgres`;
const sqlQuery = `
INSERT INTO user_preferences (user_id, key, value, updated_at)
VALUES ($1, $2, $3, $4)
ON CONFLICT (user_id, key)
DO UPDATE SET value = $3, updated_at = $4
`;
const response = await fetch(databaseUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token.access_token}`
},
body: JSON.stringify({
sql: sqlQuery,
params: [context.sessionId, key, value, new Date().toISOString()]
})
});
if (!response.ok) {
throw new Error(`Database operation failed: ${response.statusText}`);
}
return createSuccessResponse(
`✅ Preference saved: ${key} = ${value}`,
{ key, value, userId: context.sessionId }
);
} catch (error) {
return createErrorResponse(error);
}
}
);
}
Database Action API Format
All database operations go through the Hub action API:
- URL:
/hub/action/${context.deploymentId}/postgres
- Method:
POST
- Headers: Include
Authorization: Bearer ${token.access_token}
- Body:
{ "method": "POST", "url": "/postgres", "data": { "sql": "SELECT * FROM table WHERE id = $1", "params": ["value1"] } }
Context Properties
The authentication context provides these key properties:
🔑 Token Properties
- •
access_token
- OAuth access token - •
refresh_token
- Token for renewal - •
expires_at
- Token expiration - •
scopes
- Granted permissions
📊 Context Properties
- •
sessionId
- User session identifier - •
deploymentId
- MCP deployment ID - •
organizationId
- User's organization - •
userId
- Unique user identifier
Error Handling
Osiris automatically handles token refresh and authentication errors. You only need to check for the presence of authentication context:
import { createErrorResponse } from '../utils/types.js';
server.tool(
'my_tool',
'Tool with simple authentication check',
{},
async () => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
// Your tool logic here...
// Osiris handles token refresh automatically
} catch (error) {
return createErrorResponse(error);
}
}
);
Automatic Token Management
Osiris handles all token management automatically:
- ✅ Token refresh when expired
- ✅ Error messages with authentication guidance
- ✅ Scope validation and error responses
- ✅ User-friendly authentication prompts
You only need to check if (!token || !context)
in your tools.
Scoped Access
Security Through Scopes
Authentication context only provides access to services and scopes that were explicitly granted during the connect-mcp-auth
process. If your MCP tries to access a service or scope that wasn't granted, the request will fail with an authorization error.
For example, if your MCP was granted only gmail:read
scope, attempting to send emails will fail:
// ✅ This works - reading emails
const messages = await gmail.users.messages.list({ userId: 'me' });
// ❌ This fails - sending requires 'gmail:send' scope
await gmail.users.messages.send({ /* ... */ }); // Error: Insufficient scope
Tool Registration Pattern
Here's the complete pattern for registering tools in your MCP:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { getAuthContext } from '@osiris-ai/sdk';
import { createSuccessResponse, createErrorResponse } from './utils/types.js';
import { z } from 'zod';
export class MyMCP {
private hubBaseUrl: string;
constructor(hubBaseUrl: string) {
this.hubBaseUrl = hubBaseUrl;
}
public configureServer(server: McpServer): void {
// Register your tools
server.tool(
'tool_name',
'Tool description',
{
param1: z.string(),
param2: z.number().optional()
},
async ({ param1, param2 }) => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
// Your tool implementation here...
return createSuccessResponse("Operation completed", {
result: "success"
});
} catch (error) {
return createErrorResponse(error);
}
}
);
}
}
Resources and Prompts
Authentication context works the same way in resources and prompts:
// Resource example - registration would be similar with server.resource()
const getUserProfile = async () => {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return { content: "Not authenticated" };
}
// Return user-specific profile data
return {
content: `User Session: ${context.sessionId}`,
mimeType: 'text/plain'
};
};
// Prompt example - registration would be similar with server.prompt()
const getPersonalizedPrompt = async () => {
const { token, context } = getAuthContext("osiris");
return {
content: `You are helping user ${context?.sessionId || 'anonymous'}.
Be personalized and helpful based on their authenticated account data.`
};
};
Best Practices
1. Always Check Authentication
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
2. Handle Token Refresh Automatically
The Osiris Hub automatically handles token refresh—you don't need to manage this manually.
3. Use Appropriate Error Messages
Provide helpful error messages when authentication fails:
if (!token) {
return createAuthErrorResponse(
'🔐 Please connect your Google account using: npx @osiris-ai/cli connect-mcp-auth'
);
}
4. Respect Scope Limitations
Only attempt operations that are within your granted scopes. Check the token.scopes
array if needed.
5. Use Proper Response Helpers
Always use the response helper functions:
createSuccessResponse(message, data)
for successful operationscreateErrorResponse(error, data)
for general errorscreateAuthErrorResponse(message, details)
for authentication errors
The authentication context makes building authenticated MCPs effortless—no OAuth flows, no token management, just direct access to user data with proper security and scoping.