Skip to main content
Glama
mohalmah
by mohalmah

script_run

Execute a specified Google Apps Script by providing its ID and optional parameters. Streamline script management and integration with any MCP-compatible client for efficient workflow automation.

Instructions

Run a Google Apps Script.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
access_tokenNoOAuth access token.
altNoData format for response.
fieldsNoSelector specifying which fields to include in a partial response.
keyNoAPI key for the project.
oauth_tokenNoOAuth 2.0 token for the current user.
prettyPrintNoReturns response with indentations and line breaks.
quotaUserNoAvailable to use for quota purposes for server-side applications.
scriptIdYesThe ID of the script to run.

Implementation Reference

  • The main handler function `executeFunction` that sends a POST request to the Google Apps Script API endpoint to execute the specified script, handles authentication, errors, and returns the result or error details.
    const executeFunction = async ({ scriptId, fields, alt = 'json', key, access_token, oauth_token, quotaUser, prettyPrint = true }) => { const baseUrl = 'https://script.googleapis.com'; const url = new URL(`${baseUrl}/v1/scripts/${scriptId}:run`); // Append query parameters to the URL const params = new URLSearchParams({ fields, alt, key, access_token, oauth_token, quotaUser, prettyPrint: prettyPrint.toString(), '$.xgafv': '1', upload_protocol: 'raw', uploadType: 'raw' }); url.search = params.toString(); try { // Get OAuth headers const headers = await getAuthHeaders(); headers['Content-Type'] = 'application/json'; // Perform the fetch request const response = await fetch(url.toString(), { method: 'POST', headers }); // Check if the response was successful if (!response.ok) { const errorData = await response.json(); throw new Error(errorData); } // Parse and return the response data const data = await response.json(); return data; } catch (error) { const errorDetails = { message: error.message, stack: error.stack, scriptId, timestamp: new Date().toISOString(), errorType: error.name || 'Unknown' }; logger.error('SCRIPT_RUN', 'Error running the script', errorDetails); console.error('❌ Error running the script:', errorDetails); // Return detailed error information for debugging return { error: true, message: error.message, details: errorDetails, rawError: { name: error.name, stack: error.stack } }; } };
  • The input schema (parameters) for the `script_run` tool, defining the required `scriptId` and optional query parameters like `fields`, `access_token`, etc.
    parameters: { type: 'object', properties: { scriptId: { type: 'string', description: 'The ID of the script to run.' }, fields: { type: 'string', description: 'Selector specifying which fields to include in a partial response.' }, alt: { type: 'string', enum: ['json', 'xml'], description: 'Data format for response.' }, key: { type: 'string', description: 'API key for the project.' }, access_token: { type: 'string', description: 'OAuth access token.' }, oauth_token: { type: 'string', description: 'OAuth 2.0 token for the current user.' }, quotaUser: { type: 'string', description: 'Available to use for quota purposes for server-side applications.' }, prettyPrint: { type: 'boolean', description: 'Returns response with indentations and line breaks.' } }, required: ['scriptId'] }
  • The `apiTool` object export that registers the `script_run` tool by providing the handler function reference, tool name, description, and schema; this is dynamically discovered and loaded by the MCP tools system.
    const apiTool = { function: executeFunction, definition: { type: 'function', function: { name: 'script_run', description: 'Run a Google Apps Script.', parameters: { type: 'object', properties: { scriptId: { type: 'string', description: 'The ID of the script to run.' }, fields: { type: 'string', description: 'Selector specifying which fields to include in a partial response.' }, alt: { type: 'string', enum: ['json', 'xml'], description: 'Data format for response.' }, key: { type: 'string', description: 'API key for the project.' }, access_token: { type: 'string', description: 'OAuth access token.' }, oauth_token: { type: 'string', description: 'OAuth 2.0 token for the current user.' }, quotaUser: { type: 'string', description: 'Available to use for quota purposes for server-side applications.' }, prettyPrint: { type: 'boolean', description: 'Returns response with indentations and line breaks.' } }, required: ['scriptId'] } } } };
  • lib/tools.js:8-64 (registration)
    The `discoverTools` function that dynamically imports all tool files listed in `toolPaths`, extracts `apiTool`, wraps the handler with logging, and returns the list of tools for MCP server registration.
    export async function discoverTools() { logger.info('DISCOVERY', `Starting tool discovery for ${toolPaths.length} tool paths`); const toolPromises = toolPaths.map(async (file) => { try { logger.debug('DISCOVERY', `Loading tool from: ${file}`); const module = await import(`../tools/${file}`); if (!module.apiTool) { logger.warn('DISCOVERY', `Tool file missing apiTool export: ${file}`); return null; } const toolName = module.apiTool.definition?.function?.name; if (!toolName) { logger.warn('DISCOVERY', `Tool missing function name: ${file}`); return null; } // Wrap the original function with logging const originalFunction = module.apiTool.function; const wrappedFunction = withLogging(toolName, originalFunction); logger.debug('DISCOVERY', `Successfully loaded tool: ${toolName}`, { file, toolName, description: module.apiTool.definition?.function?.description }); return { ...module.apiTool, function: wrappedFunction, path: file, }; } catch (error) { logger.error('DISCOVERY', `Failed to load tool: ${file}`, { file, error: { message: error.message, stack: error.stack } }); return null; } }); const tools = (await Promise.all(toolPromises)).filter(Boolean); logger.info('DISCOVERY', `Tool discovery completed`, { totalPaths: toolPaths.length, successfullyLoaded: tools.length, failed: toolPaths.length - tools.length, toolNames: tools.map(t => t.definition?.function?.name).filter(Boolean) }); return tools; }
  • mcpServer.js:162-267 (registration)
    The MCP server setup in `run()` that calls `discoverTools()`, transforms tools for listing, and sets up `CallToolRequestSchema` handler to execute tools by name using the discovered tools array.
    const args = process.argv.slice(2); const isSSE = args.includes("--sse"); logger.info('STARTUP', `Starting MCP server in ${isSSE ? 'SSE' : 'STDIO'} mode`); const tools = await discoverTools(); logger.info('STARTUP', `Discovered ${tools.length} tools`, { toolPaths: tools.map(t => t.path), toolNames: tools.map(t => t.definition?.function?.name).filter(Boolean) }); if (isSSE) { const app = express(); const transports = {}; const servers = {}; app.get("/sse", async (_req, res) => { const sessionId = Date.now().toString(); logger.info('SSE', `New SSE connection established`, { sessionId }); // Create a new Server instance for each session const server = new Server( { name: SERVER_NAME, version: "0.1.0", }, { capabilities: { tools: {}, }, } ); server.onerror = (error) => { logger.error('SSE', `Server error for session ${sessionId}`, error); console.error("[Error]", error); }; await setupServerHandlers(server, tools); const transport = new SSEServerTransport("/messages", res); transports[transport.sessionId] = transport; servers[transport.sessionId] = server; res.on("close", async () => { logger.info('SSE', `SSE connection closed`, { sessionId: transport.sessionId }); delete transports[transport.sessionId]; await server.close(); delete servers[transport.sessionId]; }); await server.connect(transport); }); app.post("/messages", async (req, res) => { const sessionId = req.query.sessionId; const transport = transports[sessionId]; const server = servers[sessionId]; if (transport && server) { logger.debug('SSE', `Processing message for session`, { sessionId }); await transport.handlePostMessage(req, res); } else { logger.warn('SSE', `No transport/server found for session`, { sessionId }); res.status(400).send("No transport/server found for sessionId"); } }); const port = process.env.PORT || 3001; app.listen(port, () => { logger.info('SSE', `SSE server running on port ${port}`); console.log(`[SSE Server] running on port ${port}`); }); } else { // stdio mode: single server instance logger.info('STDIO', 'Initializing STDIO server'); const server = new Server( { name: SERVER_NAME, version: "0.1.0", }, { capabilities: { tools: {}, }, } ); server.onerror = (error) => { logger.error('STDIO', 'Server error', error); console.error("[Error]", error); }; await setupServerHandlers(server, tools); process.on("SIGINT", async () => { logger.info('STDIO', 'Received SIGINT, shutting down gracefully'); await server.close(); process.exit(0); }); const transport = new StdioServerTransport(); logger.info('STDIO', 'STDIO server ready for connections'); await server.connect(transport); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mohalmah/google-appscript-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server