The Azion MCP server operates as a bridge between AI assistants and the Azion Platform, providing secure access to documentation, tools, and resources through a standardized protocol.


Technical Architecture

MCP implements a client-server model using Anthropic’s Model Context Protocol. The architecture consists of three main components:

Technology Stack

ComponentTechnologyPurpose
RuntimeNode.js 20+JavaScript execution environment
LanguageTypeScriptStatic typing and safety
ProtocolMCP SDKAnthropic protocol implementation
HostingFunctionsDistributed execution on Azion
AuthenticationPersonal Token / OAuthSecure access control

Communication Flow

AI Assistant (Client)
↓ HTTP/WebSocket Connection
MCP Server (Function)
↓ API Calls
Azion Platform (Resources)

The flow works as follows:

  1. Connection: The MCP client establishes connection with the server using HTTP or WebSocket
  2. Authentication: The Personal Token validates the user’s identity
  3. Discovery: The client discovers available tools and resources
  4. Execution: The assistant invokes tools as needed
  5. Response: The server returns structured data to the client

MCP Components

The MCP protocol defines three main types of capabilities that a server can expose:

Tools

Tools are functions that the AI assistant can invoke to perform actions or fetch information. Each tool has:

  • Name: Unique identifier (e.g., search_azion_docs_and_site)
  • Description: Explains the purpose and when to use it
  • Input Schema: Defines expected parameters using JSON Schema
  • Handler: Function that executes the logic

Example tool definition:

server.tool(
'search_azion_docs_and_site',
{
description: 'Provides information about Azion Platform, products, and services',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Query to search documentation'
},
docsAmount: {
type: 'number',
description: 'Number of documents to return',
default: 5
}
},
required: ['query']
}
},
async (args) => {
// Search implementation
const results = await searchDocs(args.query, args.docsAmount);
return {
content: [{
type: 'text',
text: JSON.stringify(results)
}]
};
}
);

Resources

Resources are structured data that can be read by the client, similar to files. They use URIs for identification:

server.resource(
'static-site-deploy-guide',
'azion://static-site/deploy/step-0-preparation',
async () => ({
contents: [{
uri: 'azion://static-site/deploy/step-0-preparation',
name: 'step-0-preparation.md',
mimeType: 'text/markdown',
text: guideContent
}]
})
);

Resource URIs follow the format: azion://[category]/[resource]/[item]

Prompts

Prompts are pre-configured templates that help with common scenarios:

server.prompt(
'deploy-static-site',
{
description: 'Step-by-step guide for deploying static sites',
arguments: [
{
name: 'framework',
description: 'Framework used (e.g., nextjs, astro, hugo)',
required: true
}
]
},
async (args) => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: 'Help me deploy a ' + args.framework + ' site to Azion'
}
}]
};
}
);

Implementing an MCP Server

To implement your own MCP server, follow these steps:

Project Structure

my-mcp-server/
├── src/
│ ├── index.ts # Entry point
│ ├── core/
│ │ ├── tools.ts # Tool definitions
│ │ ├── resources.ts # Resource definitions
│ │ ├── prompts.ts # Prompt definitions
│ │ └── services/ # Business logic
│ └── middlewares/
│ └── auth.ts # Authentication
├── package.json
├── tsconfig.json
└── azion.config.ts # Deploy configuration

Entry Point

The main file initializes the server and registers capabilities:

src/index.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerTools } from './core/tools';
import { registerResources } from './core/resources';
import { registerPrompts } from './core/prompts';
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0'
});
// Register capabilities
registerTools(server);
registerResources(server);
registerPrompts(server);
// Start server
export default {
async fetch(request: Request): Promise<Response> {
return server.handleRequest(request);
}
};

Authentication

The MCP server supports multiple authentication methods:

src/middlewares/auth.ts
export async function authenticate(request: Request): Promise<AuthResult> {
const authHeader = request.headers.get('Authorization');
if (!authHeader) {
return { success: false, error: 'Token not provided' };
}
const token = authHeader.replace('Bearer ', '');
// Verify Personal Token (format: azion + 35 chars)
if (token.startsWith('azion') && token.length === 40) {
return validatePersonalToken(token);
}
// Verify OAuth token via SSO
return validateOAuthToken(token);
}

Deploying to Azion

The MCP server can be deployed as a Function on Azion:

Configuration

Create an azion.config.ts file in the project root:

azion.config.ts
export default {
build: {
preset: 'typescript',
polyfills: true
},
functions: [
{
name: '$FUNCTION_NAME',
path: './functions/index.js'
}
],
applications: [
{
name: '$APPLICATION_NAME',
rules: {
request: [
{
name: 'Execute Function',
description: 'Execute function for all requests',
active: true,
criteria: [
[
{
variable: '${uri}',
conditional: 'if',
operator: 'matches',
argument: '^/'
}
]
],
behaviors: [
{
type: 'run_function',
attributes: {
value: '$FUNCTION_NAME'
}
}
]
}
]
},
functionsInstances: [
{
name: '$FUNCTION_INSTANCE_NAME',
ref: '$FUNCTION_NAME'
}
]
}
],
workloads: [
{
name: '$WORKLOAD_NAME',
active: true,
infrastructure: 1,
deployments: [
{
name: '$DEPLOYMENT_NAME',
current: true,
active: true,
strategy: {
type: 'default',
attributes: {
application: '$APPLICATION_NAME'
}
}
}
]
}
]
}

Deploy

Use the Azion CLI to deploy:

Terminal window
# Link project
azion link
# Build project
azion build
# Deploy to production
azion deploy

The deploy creates a publicly accessible URL that can be configured in MCP clients.


Best Practices

Tool Design

  1. Descriptive names: Use clear verbs and nouns (e.g., search_docs, create_rule)
  2. Typed parameters: Define complete JSON Schema schemas
  3. Clear descriptions: Explain when and how to use each tool
  4. Error handling: Return informative error messages
  5. Idempotency: Tools should be safe for multiple calls

Performance

  1. Response caching: Implement cache for frequently accessed data
  2. Pagination: Use pagination for large datasets
  3. Timeouts: Set appropriate timeouts for long operations
  4. Compression: Compress large responses

Security

  1. Input validation: Always validate input parameters
  2. Rate limiting: Implement rate limits per token
  3. Audit logs: Log all sensitive operations
  4. Sanitization: Remove sensitive data before returning

Complete Example

See a complete example of a documentation search tool:

src/core/services/docsService.ts
import { z } from 'zod';
const SearchSchema = z.object({
query: z.string().min(1),
docsAmount: z.number().min(1).max(20).default(5)
});
export async function searchDocumentation(args: unknown) {
// Validate input
const validated = SearchSchema.parse(args);
// Search documents
const response = await fetch('https://api.azion.com/docs/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + process.env.AZION_API_KEY
},
body: JSON.stringify({
query: validated.query,
limit: validated.docsAmount
})
});
if (!response.ok) {
throw new Error('Search failed: ' + response.statusText);
}
const results = await response.json();
// Format response
return {
content: [{
type: 'text',
text: JSON.stringify(results, null, 2)
}]
};
}

Additional Resources