Glide API MCP Server
by knmurphy
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from '@modelcontextprotocol/sdk/types.js';
import axios, { AxiosInstance } from 'axios';
// Abstract base class for Glide API versions
abstract class GlideApiClient {
protected client: AxiosInstance;
constructor(apiKey: string) {
this.client = axios.create({
baseURL: this.getBaseUrl(),
headers: this.getAuthHeaders(apiKey),
});
}
abstract getBaseUrl(): string;
abstract getAuthHeaders(apiKey: string): Record<string, string>;
public async makeRequest(method: 'GET' | 'POST', endpoint: string, data?: any) {
try {
const response = await this.client.request({
method,
url: endpoint,
data,
});
return response.data;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
throw new McpError(
ErrorCode.InternalError,
`Glide API error: ${error.response?.data?.message || error.message}`
);
}
throw error;
}
}
}
// V1 API implementation
class GlideApiV1Client extends GlideApiClient {
getBaseUrl(): string {
return 'https://api.glideapp.io';
}
getAuthHeaders(apiKey: string): Record<string, string> {
return {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
};
}
}
// V2 API implementation
class GlideApiV2Client extends GlideApiClient {
getBaseUrl(): string {
return 'https://api.glideapp.com/api/v2';
}
getAuthHeaders(apiKey: string): Record<string, string> {
return {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
};
}
}
class GlideApiServer {
private server: Server;
private apiClient: GlideApiClient | null = null;
private readonly apiVersions = {
v1: GlideApiV1Client,
v2: GlideApiV2Client,
};
constructor() {
// Initialize with environment variables if available
const envApiKey = process.env.GLIDE_API_KEY;
const envApiVersion = process.env.GLIDE_API_VERSION as 'v1' | 'v2' | undefined;
if (envApiKey && envApiVersion && this.apiVersions[envApiVersion]) {
console.error(`Initializing Glide API with version ${envApiVersion} from environment`);
const ClientClass = this.apiVersions[envApiVersion];
this.apiClient = new ClientClass(envApiKey);
} else {
console.error('No environment configuration found. API version and key must be set using set_api_version tool.');
}
this.server = new Server(
{
name: 'glide-api-server',
version: '0.1.0',
},
{
capabilities: {
tools: {},
},
}
);
this.setupToolHandlers();
this.server.onerror = (error: Error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
private setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'set_api_version',
description: 'Set the Glide API version and authentication to use',
inputSchema: {
type: 'object',
properties: {
version: {
type: 'string',
enum: ['v1', 'v2'],
description: 'API version to use',
},
apiKey: {
type: 'string',
description: 'API key for authentication',
},
},
required: ['version', 'apiKey'],
},
},
{
name: 'get_app',
description: 'Get information about a Glide app',
inputSchema: {
type: 'object',
properties: {
appId: {
type: 'string',
description: 'ID of the Glide app',
},
},
required: ['appId'],
},
},
{
name: 'get_tables',
description: 'Get tables for a Glide app',
inputSchema: {
type: 'object',
properties: {
appId: {
type: 'string',
description: 'ID of the Glide app',
},
},
required: ['appId'],
},
},
{
name: 'get_table_rows',
description: 'Get rows from a table in a Glide app',
inputSchema: {
type: 'object',
properties: {
appId: {
type: 'string',
description: 'ID of the Glide app',
},
tableId: {
type: 'string',
description: 'ID of the table',
},
limit: {
type: 'number',
description: 'Maximum number of rows to return',
minimum: 1,
},
offset: {
type: 'number',
description: 'Number of rows to skip',
minimum: 0,
},
},
required: ['appId', 'tableId'],
},
},
{
name: 'add_table_row',
description: 'Add a new row to a table in a Glide app',
inputSchema: {
type: 'object',
properties: {
appId: {
type: 'string',
description: 'ID of the Glide app',
},
tableId: {
type: 'string',
description: 'ID of the table',
},
values: {
type: 'object',
description: 'Column values for the new row',
additionalProperties: true,
},
},
required: ['appId', 'tableId', 'values'],
},
},
{
name: 'update_table_row',
description: 'Update an existing row in a table',
inputSchema: {
type: 'object',
properties: {
appId: {
type: 'string',
description: 'ID of the Glide app',
},
tableId: {
type: 'string',
description: 'ID of the table',
},
rowId: {
type: 'string',
description: 'ID of the row to update',
},
values: {
type: 'object',
description: 'New column values for the row',
additionalProperties: true,
},
},
required: ['appId', 'tableId', 'rowId', 'values'],
},
},
],
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'set_api_version' && request.params.arguments) {
// Allow overriding environment variables with explicit settings
const args = request.params.arguments as {
version: 'v1' | 'v2';
apiKey: string;
};
// Validate API key is not empty
if (!args.apiKey.trim()) {
throw new McpError(
ErrorCode.InvalidParams,
'API key cannot be empty'
);
}
const ClientClass = this.apiVersions[args.version];
if (!ClientClass) {
throw new McpError(
ErrorCode.InvalidParams,
`Invalid API version: ${args.version}`
);
}
this.apiClient = new ClientClass(args.apiKey);
return {
content: [
{
type: 'text',
text: `Glide API version set to ${args.version}`,
},
],
};
}
if (!this.apiClient) {
throw new McpError(
ErrorCode.InvalidRequest,
'API version not set. Call set_api_version first.'
);
}
switch (request.params.name) {
case 'get_app': {
const { appId } = request.params.arguments as { appId: string };
const result = await this.apiClient.makeRequest('GET', `/apps/${appId}`);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
case 'get_tables': {
const { appId } = request.params.arguments as { appId: string };
const result = await this.apiClient.makeRequest('GET', `/apps/${appId}/tables`);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
case 'get_table_rows': {
const { appId, tableId, limit, offset } = request.params.arguments as {
appId: string;
tableId: string;
limit?: number;
offset?: number;
};
const params = new URLSearchParams();
if (limit) params.append('limit', limit.toString());
if (offset) params.append('offset', offset.toString());
const result = await this.apiClient.makeRequest(
'GET',
`/apps/${appId}/tables/${tableId}/rows${params.toString() ? '?' + params.toString() : ''}`
);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
case 'add_table_row': {
const { appId, tableId, values } = request.params.arguments as {
appId: string;
tableId: string;
values: Record<string, any>;
};
const result = await this.apiClient.makeRequest(
'POST',
`/apps/${appId}/tables/${tableId}/rows`,
values
);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
case 'update_table_row': {
const { appId, tableId, rowId, values } = request.params.arguments as {
appId: string;
tableId: string;
rowId: string;
values: Record<string, any>;
};
const result = await this.apiClient.makeRequest(
'POST',
`/apps/${appId}/tables/${tableId}/rows/${rowId}`,
values
);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Glide API MCP server running on stdio');
}
}
const server = new GlideApiServer();
server.run().catch(console.error);