{"name":"io.github.piotr-agier/google-drive-mcp","slug":"piotr-agier-google-drive-mcp","title":"Google Drive MCP","description":"Secure access to Google Drive, Docs, Sheets, Slides, and Calendar through MCP.","url":"https://mcp.market/server/piotr-agier-google-drive-mcp","rating":null,"grade":"B","score":83,"certified":false,"status":"active","category":"productivity","tags":["productivity"],"presence":{"score":41,"stars":226,"forks":115,"downloads_week":null,"last_push_at":"2026-10-15T11:36:33.000Z","license":"MIT"},"uptime":null,"claimed":false,"transport":"npm","callable_via_gateway":false,"default_price_micros":0,"repository":"https://github.com/piotr-agier/google-drive-mcp","website":"https://github.com/piotr-agier/google-drive-mcp#readme","version":"2.12.0","remotes":[],"packages":[{"registryType":"npm","identifier":"@piotr-agier/google-drive-mcp","version":"2.12.0","runtimeHint":"npx","transport":{"type":"stdio"},"environmentVariables":[{"description":"Path to a Google OAuth 2.0 Desktop app credentials JSON file.","isRequired":true,"format":"filepath","isSecret":true,"name":"GOOGLE_DRIVE_OAUTH_CREDENTIALS"}]}],"tools":[{"name":"addComment","description":"Add a comment quoting a text range, targeted by startIndex+endIndex or by textToFind. The comment appears in the document's comment panel and records the quoted passage. Google Docs does not support margin-anchored comments through any public API, so the comment is unanchored by design; this is a platform limit, not a failure.","write_action":false,"price_micros":0,"input_schema":null},{"name":"addDataValidation","description":"Add data validation rules to a sheet range","write_action":false,"price_micros":0,"input_schema":null},{"name":"addDimensionGroup","description":"Group a range of rows or columns in a sheet (creates a collapsible outline group). Indices are 0-based and the interval is half-open [startIndex, endIndex) \\u2014 to group rows 5 through 12 as shown in the UI, pass startIndex: 4, endIndex: 12. Nest groups by adding a second group inside the range of the first.","write_action":false,"price_micros":0,"input_schema":null},{"name":"addDocumentTab","description":"Add a new tab in a Google Doc","write_action":false,"price_micros":0,"input_schema":null},{"name":"addGoogleSheetConditionalFormat","description":"Add conditional formatting to a Google Sheet","write_action":false,"price_micros":0,"input_schema":null},{"name":"addNamedRange","description":"Create a named range","write_action":true,"price_micros":0,"input_schema":null},{"name":"addPermission","description":"Add a sharing permission to a file. If the principal already holds a permission on the file, Drive updates that permission instead of creating a second one, and the requested role is applied even when it is a downgrade (asking for 'reader' for an existing 'writer' leaves them as 'reader'). Provide emailAddress for type 'user'/'group', domain for type 'domain', and neither for 'anyone'.","write_action":false,"price_micros":0,"input_schema":null},{"name":"addSheet","description":"Alias for addSpreadsheetSheet (adds a new sheet/tab)","write_action":false,"price_micros":0,"input_schema":null},{"name":"addSpreadsheetSheet","description":"Adds a new sheet/tab to an existing Google Spreadsheet","write_action":false,"price_micros":0,"input_schema":null},{"name":"appendSpreadsheetRows","description":"Appends rows of data to the end of a sheet in a Google Spreadsheet","write_action":false,"price_micros":0,"input_schema":null},{"name":"applyParagraphStyle","description":"Apply paragraph formatting \\u2014 alignment, indents, spacing, named styles, borders (top/bottom/left/right/between), shading, and pagination controls. Use EITHER startIndex+endIndex OR textToFind OR indexWithinParagraph for targeting. The update styles EVERY paragraph overlapping the range, so one call over the whole body with removeBorders: [\"all\"] strips every paragraph border from a document.","write_action":false,"price_micros":0,"input_schema":null},{"name":"applyTextStyle","description":"Apply text formatting (bold, italic, color, etc.) to a range or found text. Use EITHER startIndex+endIndex OR textToFind for targeting.","write_action":false,"price_micros":0,"input_schema":null},{"name":"authGetStatus","description":"Show authentication/token status and scope diagnostics","write_action":false,"price_micros":0,"input_schema":null},{"name":"authListScopes","description":"List configured/requested scopes and currently granted scopes","write_action":false,"price_micros":0,"input_schema":null},{"name":"authTestFileAccess","description":"Run auth diagnostics against Drive API/file access","write_action":true,"price_micros":0,"input_schema":null},{"name":"autoResizeColumns","description":"Auto-size one or more columns to fit their content. Indices are 0-based and the interval is half-open [startColumn, endColumn) \\u2014 to auto-fit a single column at position 5, pass startColumn: 5, endColumn: 6.","write_action":false,"price_micros":0,"input_schema":null},{"name":"autoResizeRows","description":"Auto-size one or more rows to fit their content. Indices are 0-based and the interval is half-open [startRow, endRow) \\u2014 to auto-fit a single row at position 5, pass startRow: 5, endRow: 6.","write_action":false,"price_micros":0,"input_schema":null},{"name":"batchUpdateGoogleSheetValues","description":"Write many ranges in ONE call via spreadsheets.values.batchUpdate. Ranges may sit on different sheets of the same spreadsheet. Prefer this over repeated updateGoogleSheet calls whenever more than one range changes: N separate writes cost N round trips and N units of the per-minute write quota, while one batch costs one of each.","write_action":true,"price_micros":0,"input_schema":null},{"name":"bulkConvertFolderPdfs","description":"Convert all PDFs in a folder into Google Docs and return per-file results","write_action":false,"price_micros":0,"input_schema":null},{"name":"convertPdfToGoogleDoc","description":"Convert an existing PDF in Drive into an editable Google Doc","write_action":false,"price_micros":0,"input_schema":null},{"name":"copyFile","description":"Creates a copy of a Google Drive file or document","write_action":true,"price_micros":0,"input_schema":null},{"name":"createDocFromHTML","description":"Create a Google Doc from HTML content in a single Drive API call. HTML tags are converted to native Google Doc styles: <h1> \\u2192 Heading 1, <h2> \\u2192 Heading 2, <p> \\u2192 Normal Text, <b> \\u2192 bold, <i> \\u2192 italic, <ul>/<ol> \\u2192 lists, <table> \\u2192 tables. Useful when you want native heading/list/table styling applied in one request instead of a createGoogleDoc call followed by per-","write_action":true,"price_micros":0,"input_schema":null},{"name":"createFolder","description":"Create a new folder in Google Drive","write_action":true,"price_micros":0,"input_schema":null},{"name":"createFootnote","description":"Create a footnote in a Google Doc. Footnotes cannot be inserted inside equations, headers, footers, or other footnotes. For multi-tab docs, specify tabId to target a specific tab.","write_action":true,"price_micros":0,"input_schema":null},{"name":"createGoogleDoc","description":"Create a new Google Doc","write_action":true,"price_micros":0,"input_schema":null},{"name":"createGoogleSheet","description":"Create a new Google Sheet. By default uses RAW mode which stores values as-is. Set valueInputOption to 'USER_ENTERED' only when you need formulas to be evaluated.","write_action":true,"price_micros":0,"input_schema":null},{"name":"createGoogleSlides","description":"Create a new Google Slides presentation","write_action":true,"price_micros":0,"input_schema":null},{"name":"createParagraphBullets","description":"Add or remove bullet points / numbered lists on paragraphs in a Google Doc. Target paragraphs by startIndex+endIndex or textToFind. Use bulletPreset='NONE' to remove bullets.","write_action":true,"price_micros":0,"input_schema":null},{"name":"createShortcut","description":"Create a shortcut (link) to a file or folder in Google Drive. Useful for referencing the same document from multiple locations without duplicating it.","write_action":true,"price_micros":0,"input_schema":null},{"name":"createTextFile","description":"Create a new text or markdown file","write_action":true,"price_micros":0,"input_schema":null},{"name":"deleteComment","description":"Delete a comment from the document","write_action":true,"price_micros":0,"input_schema":null},{"name":"deleteDimensionGroup","description":"Remove a row or column group from a sheet. The rows/columns themselves are kept; only the grouping is removed. Indices are 0-based and the interval is half-open [startIndex, endIndex). Use listDimensionGroups to read back an existing group's range and pass it in full: the API decrements the group depth of every dimension in the range rather than matching a whole group, so a range that only partial","write_action":true,"price_micros":0,"input_schema":null},{"name":"deleteItem","description":"Move a file or folder to trash (can be restored from Google Drive trash)","write_action":true,"price_micros":0,"input_schema":null},{"name":"deleteRange","description":"Delete content between start and end indices. Works on Google Docs and text/* files (e.g. text/plain, text/markdown). Index semantics differ by file type: Google Docs use the Docs API's 1-based structural position (includes structural elements); text files use 0-based Unicode code point (character) offsets into the file content. For multi-tab Google Docs, specify tabId to target a specific tab \\u2","write_action":true,"price_micros":0,"input_schema":null},{"name":"deleteSheet","description":"Delete a sheet/tab by sheetId","write_action":true,"price_micros":0,"input_schema":null},{"name":"describeGoogleDocRange","description":"Style probe for one spot: the paragraph styles (named style, alignment, borders, shading, indents, spacing, bullets) and text runs (font, size, bold/italic/underline, colors, links) overlapping a range, with the real document index of every paragraph and run so the result feeds straight into applyParagraphStyle or applyTextStyle. Target it with startIndex (optionally endIndex) or with textToFind, ","write_action":false,"price_micros":0,"input_schema":null},{"name":"downloadFile","description":"Download a Google Drive file to a local path. For Google Workspace files (Docs, Sheets, Slides, Drawings), exports to the specified format. For regular files, downloads as-is. Streams directly to disk.","write_action":false,"price_micros":0,"input_schema":null},{"name":"editTableCell","description":"Edit the content and/or style of a specific table cell. Requires knowing the table start index. For multi-tab docs, specify tabId to target a table in a specific tab.","write_action":false,"price_micros":0,"input_schema":null},{"name":"findAndReplaceInDoc","description":"Find and replace text across a Google Document. Dry-run mode counts matches exactly across body, tables, headers, footers, and footnotes. On zero matches the response explains the likeliest lookalike cause (non-breaking spaces, curly quotes, HTML entities, case). For multi-tab docs, specify tabId to scope replacements to a single tab. When replaceText contains a newline, the write is always locked","write_action":false,"price_micros":0,"input_schema":null},{"name":"formatGoogleDocParagraph","description":"Apply paragraph formatting (alignment, indentation, spacing, heading style, borders, shading) in a Google Doc. Alias for applyParagraphStyle.","write_action":false,"price_micros":0,"input_schema":null},{"name":"formatGoogleDocText","description":"Apply text formatting (bold, italic, font, color, links, superscript/subscript) to a range or found text in a Google Doc. Alias for applyTextStyle.","write_action":false,"price_micros":0,"input_schema":null},{"name":"formatGoogleSheetCells","description":"Format cells in a Google Sheet (background, borders, alignment)","write_action":false,"price_micros":0,"input_schema":null},{"name":"formatGoogleSheetNumbers","description":"Apply number formatting to cells in a Google Sheet","write_action":false,"price_micros":0,"input_schema":null},{"name":"formatGoogleSheetText","description":"Apply text formatting to cells in a Google Sheet","write_action":false,"price_micros":0,"input_schema":null},{"name":"getComment","description":"Get a specific comment with its full thread of replies","write_action":false,"price_micros":0,"input_schema":null},{"name":"getDocumentInfo","description":"Gets detailed information about a specific Google Document.","write_action":false,"price_micros":0,"input_schema":null},{"name":"getGoogleDocContent","description":"Get content of a Google Doc with text indices for formatting","write_action":false,"price_micros":0,"input_schema":null},{"name":"getGoogleDocContentPaginated","description":"Get a portion of a Google Doc with text indices for formatting, with pagination support. Use offset and limit to read large documents in chunks.","write_action":false,"price_micros":0,"input_schema":null},{"name":"getGoogleDocImage","description":"Fetch the bytes of an inline image embedded in a Google Doc, keyed by its inline object ID. Returns a native image the model can see (or base64). The doc is re-fetched so the underlying image URL is always fresh \\u2014 pass the objectId, never a raw contentUri (those expire). Inline images only; floating/anchored images are not supported.","write_action":false,"price_micros":0,"input_schema":null},{"name":"getGoogleDocStyleSummary","description":"Compact style inventory of a whole document in a few hundred tokens: fonts with their size ladders, named-style counts, text colors, bordered and shaded paragraph locations, a table inventory with real index ranges, and the heading outline. The cheap first read before any formatting work, in place of pulling the full document JSON to answer style questions. Counts cover every tab; locations are pr","write_action":false,"price_micros":0,"input_schema":null},{"name":"getGoogleSheetCells","description":"Read Google Sheets cells as structured data instead of joined text: each cell comes back with its own absolute A1 address, and with the formula the user entered AND the value it evaluates to, in one call. Reads several ranges at once and returns them separately. Use getGoogleSheetContent for a quick human-readable dump of one range. Use this tool whenever the result will be written back, compared ","write_action":false,"price_micros":0,"input_schema":null},{"name":"getGoogleSheetContent","description":"Get content of a Google Sheet with cell information. Each row is returned as 'Row N: ' followed by that row's cells separated by tab characters. Returns displayed values by default; set valueRenderOption to 'FORMULA' to read the underlying formulas (e.g. '=SUM(A1:A10)') instead of their results, or 'UNFORMATTED_VALUE' for raw numbers without display formatting. Under UNFORMATTED_VALUE a date or ti","write_action":false,"price_micros":0,"input_schema":null},{"name":"getRevisions","description":"List revisions for a file","write_action":false,"price_micros":0,"input_schema":null},{"name":"getSpreadsheetInfo","description":"Gets detailed information about a Google Spreadsheet including all sheets/tabs","write_action":false,"price_micros":0,"input_schema":null},{"name":"hideSheetDimension","description":"Hide a range of columns or rows in a sheet (sets hiddenByUser=true). Indices are 0-based and the interval is half-open [startIndex, endIndex) \\u2014 to hide a single column/row at position 5, pass startIndex: 5, endIndex: 6.","write_action":false,"price_micros":0,"input_schema":null},{"name":"insertImageFromUrl","description":"Insert an inline image into a Google Document from a publicly accessible URL","write_action":true,"price_micros":0,"input_schema":null},{"name":"insertLocalImage","description":"Upload a local image file to Google Drive and insert it into a Google Document","write_action":true,"price_micros":0,"input_schema":null},{"name":"insertSmartChip","description":"Insert a person smart chip (mention) at a document index. Only person chips are supported by the Docs API; date and file chips are read-only.","write_action":true,"price_micros":0,"input_schema":null},{"name":"insertTable","description":"Insert a new table with the specified dimensions at a given index. For multi-tab docs, specify tabId to target a specific tab.","write_action":true,"price_micros":0,"input_schema":null},{"name":"insertText","description":"Insert text at a specific index (surgical edit, doesn't replace whole file). Works on Google Docs and text/* files (e.g. text/plain, text/markdown). Index semantics differ by file type: Google Docs use the Docs API's 1-based structural position (includes structural elements); text files use a 0-based Unicode code point (character) offset into the file content. For multi-tab Google Docs, specify ta","write_action":true,"price_micros":0,"input_schema":null},{"name":"listComments","description":"List all comments in a Google Document","write_action":false,"price_micros":0,"input_schema":null},{"name":"listDimensionGroups","description":"List the row and column groups of a spreadsheet, with each group's range, nesting depth and collapsed state. Ranges are reported as 0-based half-open intervals, exactly as addDimensionGroup, deleteDimensionGroup and updateDimensionGroup take them.","write_action":false,"price_micros":0,"input_schema":null},{"name":"listDocumentTabs","description":"List all tabs in a Google Doc with their IDs and hierarchy","write_action":false,"price_micros":0,"input_schema":null},{"name":"listFolder","description":"List contents of a folder (defaults to root)","write_action":false,"price_micros":0,"input_schema":null},{"name":"listGoogleDocs","description":"Lists Google Documents from your Google Drive with optional filtering.","write_action":false,"price_micros":0,"input_schema":null},{"name":"listGoogleSheets","description":"Lists Google Spreadsheets from your Google Drive with optional filtering","write_action":false,"price_micros":0,"input_schema":null},{"name":"listPermissions","description":"List sharing permissions for a file","write_action":false,"price_micros":0,"input_schema":null},{"name":"listSharedDrives","description":"List available Google Shared Drives","write_action":false,"price_micros":0,"input_schema":null},{"name":"listSheets","description":"List tabs/sheets in a Google Spreadsheet","write_action":false,"price_micros":0,"input_schema":null},{"name":"lockFile","description":"Lock a file to prevent editing by setting content restrictions. The file remains readable but cannot be modified until unlocked.","write_action":false,"price_micros":0,"input_schema":null},{"name":"manage_accounts","description":"Manage connected Google accounts. action='list' shows all connected accounts (no secrets). action='add' starts an OAuth flow for a new account identified by account_id (alias). action='remove' deletes local tokens for an account. action='set_default' picks which account is used when tool calls omit the `account` parameter.","write_action":false,"price_micros":0,"input_schema":null},{"name":"mergeGoogleSheetCells","description":"Merge cells in a Google Sheet","write_action":false,"price_micros":0,"input_schema":null},{"name":"moveItem","description":"Move a file or folder","write_action":false,"price_micros":0,"input_schema":null},{"name":"protectRange","description":"Protect a range in a spreadsheet","write_action":false,"price_micros":0,"input_schema":null},{"name":"readGoogleDoc","description":"Read content of a Google Doc with format options. Supports multi-tab documents. Text/markdown output leads with the document's revisionId \\u2014 pass it as ifRevisionId on write tools for an optimistic lock.","write_action":false,"price_micros":0,"input_schema":null},{"name":"readGoogleDocPaginated","description":"Read a portion of a Google Doc with pagination support. Use offset and limit to read large documents in chunks, avoiding output size limits.","write_action":false,"price_micros":0,"input_schema":null},{"name":"readSmartChips","description":"Read smart chip-like elements (person mentions, rich links, date chips) from the default tab of a document","write_action":false,"price_micros":0,"input_schema":null},{"name":"readTextFile","description":"Read content of a text file (any text/* MIME type, e.g. text/plain, text/markdown, text/csv). For Google Docs, use readGoogleDoc.","write_action":false,"price_micros":0,"input_schema":null},{"name":"removePermission","description":"Remove a permission from a file (by permissionId or emailAddress)","write_action":true,"price_micros":0,"input_schema":null},{"name":"renameDocumentTab","description":"Rename an existing Google Doc tab","write_action":false,"price_micros":0,"input_schema":null},{"name":"renameItem","description":"Rename a file or folder","write_action":false,"price_micros":0,"input_schema":null},{"name":"renameSheet","description":"Rename a sheet/tab by sheetId","write_action":false,"price_micros":0,"input_schema":null},{"name":"replyToComment","description":"Add a reply to an existing comment","write_action":false,"price_micros":0,"input_schema":null},{"name":"restoreRevision","description":"Restore a file to a selected revision (creates a new head revision). Note: workspace files (Docs, Sheets, Slides) are restored via export/import and may lose some formatting.","write_action":false,"price_micros":0,"input_schema":null},{"name":"search","description":"Search for files in Google Drive. Results are sorted most-recently-modified first by default; use orderBy for a different order. Set rawQuery=true to pass a raw Google Drive API query supporting operators like modifiedTime, createdTime, mimeType, name contains, etc.","write_action":false,"price_micros":0,"input_schema":null},{"name":"setColumnWidth","description":"Set the width (in pixels) of one or more columns in a sheet. Indices are 0-based and the interval is half-open [startColumn, endColumn) \\u2014 to resize a single column at position 5, pass startColumn: 5, endColumn: 6.","write_action":false,"price_micros":0,"input_schema":null},{"name":"setGoogleSheetBorders","description":"Set borders for cells in a Google Sheet","write_action":false,"price_micros":0,"input_schema":null},{"name":"setRowHeight","description":"Set the height (in pixels) of one or more rows in a sheet. Indices are 0-based and the interval is half-open [startRow, endRow) \\u2014 to resize a single row at position 5, pass startRow: 5, endRow: 6.","write_action":false,"price_micros":0,"input_schema":null},{"name":"shareFile","description":"Convenience wrapper to share a file with a user email. If the user already holds a permission on the file, that permission's role is set to the requested one (even when it is a downgrade) instead of creating a duplicate.","write_action":false,"price_micros":0,"input_schema":null},{"name":"showSheetDimension","description":"Show (unhide) a range of columns or rows in a sheet (sets hiddenByUser=false). Indices are 0-based and the interval is half-open [startIndex, endIndex) \\u2014 to unhide a single column/row at position 5, pass startIndex: 5, endIndex: 6.","write_action":false,"price_micros":0,"input_schema":null},{"name":"styleDocTable","description":"Style a Docs table in one atomic call: cell borders/background/padding/content alignment (whole table via tableStartLocation, or a cell range subset), fixed column widths, and minimum row heights. Get the real tableStartIndex from the table span line readGoogleDoc or getGoogleDocContent prints (tableStartIndex=N) \\u2014 never compute it from text offsets. removeBorders: [\"all\"] unboxes the table (","write_action":false,"price_micros":0,"input_schema":null},{"name":"unlockFile","description":"Unlock a previously locked file by removing content restrictions, restoring full edit access.","write_action":false,"price_micros":0,"input_schema":null},{"name":"updateDimensionGroup","description":"Collapse or expand an existing row or column group. Indices are 0-based and the interval is half-open [startIndex, endIndex). Use listDimensionGroups first to find the group's range and depth.","write_action":true,"price_micros":0,"input_schema":null},{"name":"updateGoogleDoc","description":"Update an existing Google Doc (replaces all content). The replacement is one atomic batchUpdate, so a failure leaves the document unchanged rather than wiped. For multi-tab docs, specify tabId to replace a single tab's content; leaves other tabs untouched.","write_action":true,"price_micros":0,"input_schema":null},{"name":"updateGoogleSheet","description":"Update an existing Google Sheet. By default uses RAW mode which stores values as-is. Set valueInputOption to 'USER_ENTERED' only when you need formulas to be evaluated.","write_action":true,"price_micros":0,"input_schema":null},{"name":"updateGoogleSheetIfUnchanged","description":"Write cell values only if the guarded area has not changed since you read it, and get back what was overwritten so the change can be undone. Call once with dryRun:true to obtain the fingerprint, then again passing it as expectedFingerprint. IMPORTANT: this is optimistic and NOT atomic. The Sheets API has no compare-and-swap - unlike Docs, where ifRevisionId is enforced by the API itself - so the c","write_action":true,"price_micros":0,"input_schema":null},{"name":"updatePermission","description":"Update an existing permission role","write_action":true,"price_micros":0,"input_schema":null},{"name":"updateTextFile","description":"Update an existing text or markdown file","write_action":true,"price_micros":0,"input_schema":null},{"name":"uploadFile","description":"Upload a file (any type: image, audio, video, PDF, etc.) to Google Drive, either from a local path on the server or from base64-encoded content. When fileId is provided, uploads the content as a new version of that existing file (in-place update) instead of creating a new file.","write_action":true,"price_micros":0,"input_schema":null},{"name":"uploadPdfWithSplit","description":"Upload PDF and optionally split into chunked parts (metadata split plan for now)","write_action":true,"price_micros":0,"input_schema":null}],"scan":{"score":83,"grade":"B","scanned_at":"2026-10-15T11:59:53.776Z","report":{"scannerVersion":"0.1.10","scannedAt":"2026-10-15T11:59:53.734Z","components":{"code":{"score":25,"max":25,"notes":["2 source files scanned"]},"reliability":{"score":-1,"max":20,"notes":["no gateway calls yet and no remote to probe"]},"poisoning":{"score":-1,"max":15,"notes":["tools not inspected (local package is not executed); not counted"]},"auth":{"score":6,"max":15,"notes":["static API keys via environment variables"]},"maintenance":{"score":15,"max":15,"notes":["last push 0 days ago"]},"identity":{"score":8,"max":10,"notes":["registry namespace matches repository owner","GitHub account older than a year"]}},"findings":[],"inputs":{"packages":[{"registryType":"npm","identifier":"@piotr-agier/google-drive-mcp","version":"2.12.0","found":true,"license":"MIT","hasInstallScripts":false,"dependencyCount":11,"publishedAt":"2026-09-25T05:53:37.034Z","repositoryUrl":"git+https://github.com/piotr-agier/google-drive-mcp.git"}],"repo":{"found":true,"owner":"piotr-agier","repo":"google-drive-mcp","archived":false,"pushedAt":"2026-10-15T11:36:33Z","stars":226,"forks":115,"openIssues":1,"ownerType":"User","ownerAvatarUrl":"https://avatars.githubusercontent.com/u/9801497?v=4","ownerCreatedAt":"2014-11-17T13:16:18Z","license":"MIT"},"icon":{"url":"https://avatars.githubusercontent.com/u/9801497?v=4&s=128","source":"registry","width":128,"height":128},"presence":{"stars":226,"forks":115,"downloadsWeek":null,"license":"MIT","lastPushAt":"2026-10-15T11:36:33.000Z","score":41}}}},"grade_history":[],"reviews":[]}