# Directory Structure
```
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
├── src
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | node_modules/
2 | build/
3 | *.log
4 | .env*
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Glide API MCP Server
2 |
3 | A Model Context Protocol server for interacting with the Glide API (v1 & v2).
4 |
5 | ## Features
6 |
7 | - Support for both Glide API v1 and v2
8 | - Secure API key handling through environment variables
9 | - Type-safe TypeScript implementation
10 | - Comprehensive error handling
11 |
12 | ## Available Tools
13 |
14 | - `set_api_version`: Configure API version and authentication
15 | - `get_app`: Get app information
16 | - `get_tables`: List app tables
17 | - `get_table_rows`: Get table data
18 | - `add_table_row`: Add new row
19 | - `update_table_row`: Update existing row
20 |
21 | ## Secure Setup
22 |
23 | ### 1. Environment Variables
24 |
25 | The server supports secure configuration through environment variables in the MCP settings file. Add your API credentials to the MCP settings file:
26 |
27 | ```json
28 | {
29 | "mcpServers": {
30 | "glide-api": {
31 | "command": "node",
32 | "args": ["path/to/build/index.js"],
33 | "env": {
34 | "GLIDE_API_KEY": "your-api-key-here",
35 | "GLIDE_API_VERSION": "v2" // or "v1" for v1 API
36 | }
37 | }
38 | }
39 | }
40 | ```
41 |
42 | This approach keeps your API key secure by:
43 | - Storing it in a configuration file rather than in code
44 | - Keeping it out of version control
45 | - Making it easy to update without modifying code
46 |
47 | ### 2. Runtime Configuration
48 |
49 | While environment variables are the recommended way to configure the server, you can also set or override the API version and key at runtime using the `set_api_version` tool:
50 |
51 | ```typescript
52 | use_mcp_tool({
53 | server_name: "glide-api",
54 | tool_name: "set_api_version",
55 | arguments: {
56 | version: "v2",
57 | apiKey: "your-api-key"
58 | }
59 | });
60 | ```
61 |
62 | Note: The runtime configuration will override any environment variables for the current session.
63 |
64 | ### 3. Security Best Practices
65 |
66 | 1. Never commit API keys to version control
67 | 2. Use environment variables in the MCP settings file
68 | 3. Regularly rotate your API keys
69 | 4. Set appropriate file permissions on the settings file
70 |
71 | ## Development
72 |
73 | Install dependencies:
74 | ```bash
75 | npm install
76 | ```
77 |
78 | Build the server:
79 | ```bash
80 | npm run build
81 | ```
82 |
83 | For development with auto-rebuild:
84 | ```bash
85 | npm run watch
86 | ```
87 |
88 | ## Usage Examples
89 |
90 | 1. Get app information:
91 | ```typescript
92 | use_mcp_tool({
93 | server_name: "glide-api",
94 | tool_name: "get_app",
95 | arguments: {
96 | appId: "your-app-id"
97 | }
98 | });
99 | ```
100 |
101 | 2. Add a row to a table:
102 | ```typescript
103 | use_mcp_tool({
104 | server_name: "glide-api",
105 | tool_name: "add_table_row",
106 | arguments: {
107 | appId: "your-app-id",
108 | tableId: "your-table-id",
109 | values: {
110 | column1: "value1",
111 | column2: "value2"
112 | }
113 | }
114 | });
115 |
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "module": "ES2020",
5 | "moduleResolution": "node",
6 | "esModuleInterop": true,
7 | "strict": true,
8 | "outDir": "build",
9 | "rootDir": "src",
10 | "declaration": true,
11 | "skipLibCheck": true
12 | },
13 | "include": ["src/**/*"],
14 | "exclude": ["node_modules", "build"]
15 | }
16 |
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "glide-api-mcp-server",
3 | "version": "0.1.0",
4 | "description": "MCP server for interacting with Glide API v1 and v2",
5 | "type": "module",
6 | "main": "build/index.js",
7 | "scripts": {
8 | "build": "tsc && chmod +x build/index.js",
9 | "watch": "tsc -w",
10 | "start": "node build/index.js"
11 | },
12 | "dependencies": {
13 | "@modelcontextprotocol/sdk": "^1.0.4",
14 | "axios": "^1.6.2"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.10.0",
18 | "typescript": "^5.3.2"
19 | }
20 | }
21 |
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | #!/usr/bin/env node
2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4 | import {
5 | CallToolRequestSchema,
6 | ErrorCode,
7 | ListToolsRequestSchema,
8 | McpError,
9 | } from '@modelcontextprotocol/sdk/types.js';
10 | import axios, { AxiosInstance } from 'axios';
11 |
12 | // Abstract base class for Glide API versions
13 | abstract class GlideApiClient {
14 | protected client: AxiosInstance;
15 |
16 | constructor(apiKey: string) {
17 | this.client = axios.create({
18 | baseURL: this.getBaseUrl(),
19 | headers: this.getAuthHeaders(apiKey),
20 | });
21 | }
22 |
23 | abstract getBaseUrl(): string;
24 | abstract getAuthHeaders(apiKey: string): Record<string, string>;
25 |
26 | public async makeRequest(method: 'GET' | 'POST', endpoint: string, data?: any) {
27 | try {
28 | const response = await this.client.request({
29 | method,
30 | url: endpoint,
31 | data,
32 | });
33 | return response.data;
34 | } catch (error: unknown) {
35 | if (axios.isAxiosError(error)) {
36 | throw new McpError(
37 | ErrorCode.InternalError,
38 | `Glide API error: ${error.response?.data?.message || error.message}`
39 | );
40 | }
41 | throw error;
42 | }
43 | }
44 | }
45 |
46 | // V1 API implementation
47 | class GlideApiV1Client extends GlideApiClient {
48 | getBaseUrl(): string {
49 | return 'https://api.glideapp.io';
50 | }
51 |
52 | getAuthHeaders(apiKey: string): Record<string, string> {
53 | return {
54 | 'X-API-Key': apiKey,
55 | 'Content-Type': 'application/json',
56 | };
57 | }
58 | }
59 |
60 | // V2 API implementation
61 | class GlideApiV2Client extends GlideApiClient {
62 | getBaseUrl(): string {
63 | return 'https://api.glideapp.com/api/v2';
64 | }
65 |
66 | getAuthHeaders(apiKey: string): Record<string, string> {
67 | return {
68 | 'Authorization': `Bearer ${apiKey}`,
69 | 'Content-Type': 'application/json',
70 | };
71 | }
72 | }
73 |
74 | class GlideApiServer {
75 | private server: Server;
76 | private apiClient: GlideApiClient | null = null;
77 | private readonly apiVersions = {
78 | v1: GlideApiV1Client,
79 | v2: GlideApiV2Client,
80 | };
81 |
82 | constructor() {
83 | // Initialize with environment variables if available
84 | const envApiKey = process.env.GLIDE_API_KEY;
85 | const envApiVersion = process.env.GLIDE_API_VERSION as 'v1' | 'v2' | undefined;
86 |
87 | if (envApiKey && envApiVersion && this.apiVersions[envApiVersion]) {
88 | console.error(`Initializing Glide API with version ${envApiVersion} from environment`);
89 | const ClientClass = this.apiVersions[envApiVersion];
90 | this.apiClient = new ClientClass(envApiKey);
91 | } else {
92 | console.error('No environment configuration found. API version and key must be set using set_api_version tool.');
93 | }
94 |
95 | this.server = new Server(
96 | {
97 | name: 'glide-api-server',
98 | version: '0.1.0',
99 | },
100 | {
101 | capabilities: {
102 | tools: {},
103 | },
104 | }
105 | );
106 |
107 | this.setupToolHandlers();
108 |
109 | this.server.onerror = (error: Error) => console.error('[MCP Error]', error);
110 | process.on('SIGINT', async () => {
111 | await this.server.close();
112 | process.exit(0);
113 | });
114 | }
115 |
116 | private setupToolHandlers() {
117 | this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
118 | tools: [
119 | {
120 | name: 'set_api_version',
121 | description: 'Set the Glide API version and authentication to use',
122 | inputSchema: {
123 | type: 'object',
124 | properties: {
125 | version: {
126 | type: 'string',
127 | enum: ['v1', 'v2'],
128 | description: 'API version to use',
129 | },
130 | apiKey: {
131 | type: 'string',
132 | description: 'API key for authentication',
133 | },
134 | },
135 | required: ['version', 'apiKey'],
136 | },
137 | },
138 | {
139 | name: 'get_app',
140 | description: 'Get information about a Glide app',
141 | inputSchema: {
142 | type: 'object',
143 | properties: {
144 | appId: {
145 | type: 'string',
146 | description: 'ID of the Glide app',
147 | },
148 | },
149 | required: ['appId'],
150 | },
151 | },
152 | {
153 | name: 'get_tables',
154 | description: 'Get tables for a Glide app',
155 | inputSchema: {
156 | type: 'object',
157 | properties: {
158 | appId: {
159 | type: 'string',
160 | description: 'ID of the Glide app',
161 | },
162 | },
163 | required: ['appId'],
164 | },
165 | },
166 | {
167 | name: 'get_table_rows',
168 | description: 'Get rows from a table in a Glide app',
169 | inputSchema: {
170 | type: 'object',
171 | properties: {
172 | appId: {
173 | type: 'string',
174 | description: 'ID of the Glide app',
175 | },
176 | tableId: {
177 | type: 'string',
178 | description: 'ID of the table',
179 | },
180 | limit: {
181 | type: 'number',
182 | description: 'Maximum number of rows to return',
183 | minimum: 1,
184 | },
185 | offset: {
186 | type: 'number',
187 | description: 'Number of rows to skip',
188 | minimum: 0,
189 | },
190 | },
191 | required: ['appId', 'tableId'],
192 | },
193 | },
194 | {
195 | name: 'add_table_row',
196 | description: 'Add a new row to a table in a Glide app',
197 | inputSchema: {
198 | type: 'object',
199 | properties: {
200 | appId: {
201 | type: 'string',
202 | description: 'ID of the Glide app',
203 | },
204 | tableId: {
205 | type: 'string',
206 | description: 'ID of the table',
207 | },
208 | values: {
209 | type: 'object',
210 | description: 'Column values for the new row',
211 | additionalProperties: true,
212 | },
213 | },
214 | required: ['appId', 'tableId', 'values'],
215 | },
216 | },
217 | {
218 | name: 'update_table_row',
219 | description: 'Update an existing row in a table',
220 | inputSchema: {
221 | type: 'object',
222 | properties: {
223 | appId: {
224 | type: 'string',
225 | description: 'ID of the Glide app',
226 | },
227 | tableId: {
228 | type: 'string',
229 | description: 'ID of the table',
230 | },
231 | rowId: {
232 | type: 'string',
233 | description: 'ID of the row to update',
234 | },
235 | values: {
236 | type: 'object',
237 | description: 'New column values for the row',
238 | additionalProperties: true,
239 | },
240 | },
241 | required: ['appId', 'tableId', 'rowId', 'values'],
242 | },
243 | },
244 | ],
245 | }));
246 |
247 | this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
248 | if (request.params.name === 'set_api_version' && request.params.arguments) {
249 | // Allow overriding environment variables with explicit settings
250 | const args = request.params.arguments as {
251 | version: 'v1' | 'v2';
252 | apiKey: string;
253 | };
254 |
255 | // Validate API key is not empty
256 | if (!args.apiKey.trim()) {
257 | throw new McpError(
258 | ErrorCode.InvalidParams,
259 | 'API key cannot be empty'
260 | );
261 | }
262 |
263 | const ClientClass = this.apiVersions[args.version];
264 | if (!ClientClass) {
265 | throw new McpError(
266 | ErrorCode.InvalidParams,
267 | `Invalid API version: ${args.version}`
268 | );
269 | }
270 |
271 | this.apiClient = new ClientClass(args.apiKey);
272 |
273 | return {
274 | content: [
275 | {
276 | type: 'text',
277 | text: `Glide API version set to ${args.version}`,
278 | },
279 | ],
280 | };
281 | }
282 |
283 | if (!this.apiClient) {
284 | throw new McpError(
285 | ErrorCode.InvalidRequest,
286 | 'API version not set. Call set_api_version first.'
287 | );
288 | }
289 |
290 | switch (request.params.name) {
291 | case 'get_app': {
292 | const { appId } = request.params.arguments as { appId: string };
293 | const result = await this.apiClient.makeRequest('GET', `/apps/${appId}`);
294 | return {
295 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
296 | };
297 | }
298 |
299 | case 'get_tables': {
300 | const { appId } = request.params.arguments as { appId: string };
301 | const result = await this.apiClient.makeRequest('GET', `/apps/${appId}/tables`);
302 | return {
303 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
304 | };
305 | }
306 |
307 | case 'get_table_rows': {
308 | const { appId, tableId, limit, offset } = request.params.arguments as {
309 | appId: string;
310 | tableId: string;
311 | limit?: number;
312 | offset?: number;
313 | };
314 | const params = new URLSearchParams();
315 | if (limit) params.append('limit', limit.toString());
316 | if (offset) params.append('offset', offset.toString());
317 |
318 | const result = await this.apiClient.makeRequest(
319 | 'GET',
320 | `/apps/${appId}/tables/${tableId}/rows${params.toString() ? '?' + params.toString() : ''}`
321 | );
322 | return {
323 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
324 | };
325 | }
326 |
327 | case 'add_table_row': {
328 | const { appId, tableId, values } = request.params.arguments as {
329 | appId: string;
330 | tableId: string;
331 | values: Record<string, any>;
332 | };
333 | const result = await this.apiClient.makeRequest(
334 | 'POST',
335 | `/apps/${appId}/tables/${tableId}/rows`,
336 | values
337 | );
338 | return {
339 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
340 | };
341 | }
342 |
343 | case 'update_table_row': {
344 | const { appId, tableId, rowId, values } = request.params.arguments as {
345 | appId: string;
346 | tableId: string;
347 | rowId: string;
348 | values: Record<string, any>;
349 | };
350 | const result = await this.apiClient.makeRequest(
351 | 'POST',
352 | `/apps/${appId}/tables/${tableId}/rows/${rowId}`,
353 | values
354 | );
355 | return {
356 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
357 | };
358 | }
359 |
360 | default:
361 | throw new McpError(
362 | ErrorCode.MethodNotFound,
363 | `Unknown tool: ${request.params.name}`
364 | );
365 | }
366 | });
367 | }
368 |
369 | async run() {
370 | const transport = new StdioServerTransport();
371 | await this.server.connect(transport);
372 | console.error('Glide API MCP server running on stdio');
373 | }
374 | }
375 |
376 | const server = new GlideApiServer();
377 | server.run().catch(console.error);
378 |
```