air
Enables local Ollama LLMs to call tools defined in the server via Ollama's function-calling API.
air
TypeScript framework for building MCP servers.
One function per tool. One line per plugin. Security, caching, retry — all built in.
npm install @airmcp-dev/coreWhy air?
Building an MCP server with the SDK directly requires ~70 lines of boilerplate per tool: Zod schemas, manual content wrapping, try/catch in every handler, transport setup. air reduces it to what matters.
MCP SDK (direct):
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const server = new McpServer({ name: 'my-server', version: '0.1.0' });
server.tool('search', 'Search docs', { query: z.string(), limit: z.number().optional() },
async ({ query, limit }) => {
try {
const results = await doSearch(query, limit);
return { content: [{ type: 'text', text: JSON.stringify(results) }] };
} catch (error) {
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true };
}
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
// retry? cache? auth? rate limit? logging? implement them all yourselfair:
import { defineServer, defineTool, retryPlugin, cachePlugin, authPlugin } from '@airmcp-dev/core';
const server = defineServer({
name: 'my-server',
transport: { type: 'sse', port: 3510 },
use: [
retryPlugin({ maxRetries: 3 }),
cachePlugin({ ttlMs: 60_000 }),
authPlugin({ type: 'api-key', keys: [process.env.MCP_API_KEY!] }),
],
tools: [
defineTool('search', {
description: 'Search docs',
params: { query: 'string', limit: 'number?' },
handler: async ({ query, limit }) => doSearch(query, limit),
}),
],
});
server.start();Related MCP server: MCP Example Simple Server
Quick Start
npx @airmcp-dev/cli create my-server
cd my-server && npm install
npx @airmcp-dev/cli dev --console -p 3510Real-World Examples
REST API Wrapper
Wrap any REST API as MCP tools in 30 lines:
import { defineServer, defineTool, cachePlugin, timeoutPlugin } from '@airmcp-dev/core';
const server = defineServer({
name: 'weather-api',
transport: { type: 'sse', port: 3510 },
use: [timeoutPlugin(5000), cachePlugin({ ttlMs: 300_000 })],
tools: [
defineTool('get_weather', {
description: 'Get current weather for a city',
params: { city: 'string' },
handler: async ({ city }) => {
const res = await fetch(`https://wttr.in/${city}?format=j1`);
const data = await res.json();
return {
city,
temp: data.current_condition[0].temp_C + '°C',
desc: data.current_condition[0].weatherDesc[0].value,
};
},
}),
],
});
server.start();Note Manager with Persistent Storage
import { defineServer, defineTool, createStorage, onShutdown } from '@airmcp-dev/core';
const store = await createStorage({ type: 'file', path: '.air/notes' });
const server = defineServer({
name: 'notes',
tools: [
defineTool('create_note', {
params: { title: 'string', content: 'string', tags: 'string?' },
handler: async ({ title, content, tags }) => {
const id = `n_${Date.now()}`;
await store.set('notes', id, { id, title, content, tags: tags?.split(',') ?? [], createdAt: new Date().toISOString() });
return { id, message: `"${title}" created` };
},
}),
defineTool('search_notes', {
params: { query: 'string' },
handler: async ({ query }) => {
const entries = await store.entries('notes');
return entries.filter(e => e.value.title.includes(query) || e.value.content.includes(query))
.map(e => ({ id: e.key, title: e.value.title }));
},
}),
defineTool('list_notes', {
handler: async () => {
const entries = await store.entries('notes');
return { count: entries.length, notes: entries.map(e => ({ id: e.key, title: e.value.title, tags: e.value.tags })) };
},
}),
],
});
onShutdown(async () => { await store.close(); });
server.start();Connect to Claude Desktop
# Register the server
npx @airmcp-dev/cli connect claude-desktop
# Or manually add to claude_desktop_config.json:{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/absolute/path/to/dist/index.js"]
}
}
}Connect to Local LLM (Ollama)
air servers work with any MCP-compatible client. With Ollama, use the tool definitions as function calling:
// 1. Define tools in air
const server = defineServer({
name: 'my-tools',
tools: [
defineTool('calc', {
params: { a: 'number', op: 'string', b: 'number?' },
handler: async ({ a, op, b }) => { /* ... */ },
}),
],
});
// 2. Send tool definitions to Ollama API
const response = await fetch('http://localhost:11434/api/chat', {
method: 'POST',
body: JSON.stringify({
model: 'llama3.1',
messages: [{ role: 'user', content: 'What is 42 * 58?' }],
tools: [{ type: 'function', function: { name: 'calc', parameters: { ... } } }],
}),
});
// 3. Execute tool calls through air
const toolCall = response.message.tool_calls[0];
const result = await server.callTool(toolCall.function.name, toolCall.function.arguments);19 Built-in Plugins
Add to the use array. Order matters — first plugin runs first.
use: [
authPlugin({ type: 'api-key', keys: [process.env.MCP_API_KEY!] }), // auth first
sanitizerPlugin(),
timeoutPlugin(10_000),
retryPlugin({ maxRetries: 3 }),
cachePlugin({ ttlMs: 60_000 }),
queuePlugin({ concurrency: { 'heavy-task': 3, '*': 10 } }),
]Category | Plugins |
Stability | timeoutPlugin, retryPlugin, circuitBreakerPlugin, fallbackPlugin |
Performance | cachePlugin, dedupPlugin, queuePlugin |
Security | authPlugin, sanitizerPlugin, validatorPlugin |
Network | corsPlugin, webhookPlugin |
Data | transformPlugin, i18nPlugin |
Monitoring | jsonLoggerPlugin, perUserRateLimitPlugin |
Dev/Test | dryrunPlugin |
Transport
transport: { type: 'stdio' } // Claude Desktop (default)
transport: { type: 'sse', port: 3510 } // Remote SSE
transport: { type: 'http', port: 3510 } // Streamable HTTP
transport: { type: 'workers' } // Cloudflare Workers (edge)Auto-detection: piped stdin → stdio, terminal → http.
Storage
const store = await createStorage({ type: 'memory' }); // In-memory (default)
const store = await createStorage({ type: 'file', path: '.air/data' }); // JSON + JSONL
await store.set('users', 'u1', { name: 'Alice' }, 3600); // TTL in seconds
await store.get('users', 'u1'); // { name: 'Alice' }
await store.append('logs', { action: 'login' }); // Append-only log
await store.query('logs', { limit: 50, filter: { action: 'login' } });CLI
npx @airmcp-dev/cli create <name> Create project (templates: basic, api, crud, agent)
npx @airmcp-dev/cli dev --console Dev mode with hot reload
npx @airmcp-dev/cli connect <client> Register with Claude Desktop, Cursor, VS Code, etc.
npx @airmcp-dev/cli status Show running servers
npx @airmcp-dev/cli inspect <tool> Inspect tool schemaSupported clients: claude-desktop, claude-code, cursor, vscode, chatgpt, ollama, vllm, lm-studio
Packages
Package | Description | License |
Server, tools, 19 plugins, storage, transport | Apache-2.0 | |
CLI (create, dev, connect, inspect) | Apache-2.0 | |
Multi-server proxy, load balancing, health checks | Apache-2.0 | |
Structured logging, file rotation | Apache-2.0 | |
7-layer call classification, cost tracking | Apache-2.0 | |
@airmcp-dev/shield | Threat detection, policy engine, audit log | Commercial |
@airmcp-dev/hive | Process pool, auto-restart, multi-tenant | Commercial |
Documentation
Full documentation: docs.airmcp.dev
110 pages, English & Korean
Source-verified API reference
9 real-world examples
Plugin detailed guides
Architecture diagrams
Troubleshooting & FAQ
Tests
pnpm install
npx vitest run # 28 files, 333 testsLicense
Open source packages: Apache-2.0 Enterprise packages (shield, hive): Commercial License
Built by CodePedia Labs · Docs · npm
This server cannot be deployed
Maintenance
Related MCP Connectors
Security scanner for MCP servers. Detect vulnerabilities, prompt injection, and tool poisoning.
MCP server for developers documentation, generated by doc2mcp.
MCP registry & directory: search, find & install 31k+ MCP servers & tools. Catalog and marketplace.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceDamn Vulnerable MCP Server for Security Researchers.7-
- FlicenseNot gradedqualityDmaintenanceA basic example MCP server built with the FastMCP framework for demonstration and learning purposes. Provides a starting point for developers to understand MCP server implementation.-
- FlicenseNot gradedqualityDmaintenanceAn educational MCP server demonstrating common security vulnerabilities like command injection, path traversal, SQL injection, and XXE attacks. Designed for security training purposes only, not for production use.-
- FlicenseNot gradedqualityDmaintenanceA Model Context Protocol (MCP) server designed for learning and experimentation. It provides a foundational setup for developers to build, run, and debug MCP server implementations using Node.js.-