aios-core 2.1.6 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.aios-core/core/README.md +229 -229
- package/.aios-core/core/data/agent-config-requirements.yaml +368 -368
- package/.aios-core/core/data/aios-kb.md +923 -923
- package/.aios-core/core/data/workflow-patterns.yaml +267 -267
- package/.aios-core/core/docs/SHARD-TRANSLATION-GUIDE.md +335 -335
- package/.aios-core/core/docs/component-creation-guide.md +457 -457
- package/.aios-core/core/docs/session-update-pattern.md +307 -307
- package/.aios-core/core/docs/template-syntax.md +266 -266
- package/.aios-core/core/docs/troubleshooting-guide.md +624 -624
- package/.aios-core/core/elicitation/elicitation-engine.js +1 -1
- package/.aios-core/core/index.esm.js +42 -42
- package/.aios-core/core/index.js +1 -1
- package/.aios-core/core/migration/migration-config.yaml +83 -83
- package/.aios-core/core/migration/module-mapping.yaml +89 -89
- package/.aios-core/core/quality-gates/layer2-pr-automation.js +1 -1
- package/.aios-core/core/quality-gates/quality-gate-config.yaml +86 -86
- package/.aios-core/core/registry/README.md +179 -179
- package/.aios-core/core/utils/security-utils.js +1 -1
- package/.aios-core/core-config.yaml +391 -382
- package/.aios-core/data/agent-config-requirements.yaml +368 -368
- package/.aios-core/data/aios-kb.md +923 -923
- package/.aios-core/data/technical-preferences.md +3 -3
- package/.aios-core/data/workflow-patterns.yaml +267 -267
- package/.aios-core/development/README.md +142 -142
- package/.aios-core/development/agent-teams/team-all.yaml +15 -15
- package/.aios-core/development/agent-teams/team-fullstack.yaml +18 -18
- package/.aios-core/development/agent-teams/team-ide-minimal.yaml +10 -10
- package/.aios-core/development/agent-teams/team-no-ui.yaml +13 -13
- package/.aios-core/development/agent-teams/team-qa-focused.yaml +155 -155
- package/.aios-core/development/agents/aios-master.md +339 -339
- package/.aios-core/development/agents/analyst.md +195 -195
- package/.aios-core/development/agents/architect.md +359 -359
- package/.aios-core/development/agents/data-engineer.md +468 -468
- package/.aios-core/development/agents/dev.md +390 -390
- package/.aios-core/development/agents/devops.md +398 -398
- package/.aios-core/development/agents/pm.md +198 -198
- package/.aios-core/development/agents/po.md +256 -256
- package/.aios-core/development/agents/qa.md +312 -312
- package/.aios-core/development/agents/sm.md +220 -220
- package/.aios-core/development/agents/ux-design-expert.md +451 -451
- package/.aios-core/development/scripts/greeting-config-cli.js +85 -85
- package/.aios-core/development/tasks/add-mcp.md +319 -319
- package/.aios-core/development/tasks/advanced-elicitation.md +318 -318
- package/.aios-core/development/tasks/analyst-facilitate-brainstorming.md +341 -341
- package/.aios-core/development/tasks/analyze-framework.md +696 -696
- package/.aios-core/development/tasks/analyze-performance.md +637 -637
- package/.aios-core/development/tasks/apply-qa-fixes.md +340 -340
- package/.aios-core/development/tasks/architect-analyze-impact.md +826 -826
- package/.aios-core/development/tasks/audit-codebase.md +429 -429
- package/.aios-core/development/tasks/audit-tailwind-config.md +270 -270
- package/.aios-core/development/tasks/audit-utilities.md +358 -358
- package/.aios-core/development/tasks/bootstrap-shadcn-library.md +286 -286
- package/.aios-core/development/tasks/brownfield-create-epic.md +485 -485
- package/.aios-core/development/tasks/brownfield-create-story.md +356 -356
- package/.aios-core/development/tasks/build-component.md +478 -478
- package/.aios-core/development/tasks/calculate-roi.md +455 -455
- package/.aios-core/development/tasks/ci-cd-configuration.md +764 -764
- package/.aios-core/development/tasks/cleanup-utilities.md +670 -670
- package/.aios-core/development/tasks/collaborative-edit.md +1108 -1108
- package/.aios-core/development/tasks/compose-molecule.md +284 -284
- package/.aios-core/development/tasks/consolidate-patterns.md +414 -414
- package/.aios-core/development/tasks/correct-course.md +279 -279
- package/.aios-core/development/tasks/create-agent.md +321 -321
- package/.aios-core/development/tasks/create-brownfield-story.md +726 -726
- package/.aios-core/development/tasks/create-deep-research-prompt.md +498 -498
- package/.aios-core/development/tasks/create-doc.md +316 -316
- package/.aios-core/development/tasks/create-next-story.md +774 -774
- package/.aios-core/development/tasks/create-suite.md +283 -283
- package/.aios-core/development/tasks/create-task.md +371 -371
- package/.aios-core/development/tasks/create-workflow.md +370 -370
- package/.aios-core/development/tasks/db-analyze-hotpaths.md +572 -572
- package/.aios-core/development/tasks/db-apply-migration.md +381 -381
- package/.aios-core/development/tasks/db-bootstrap.md +642 -642
- package/.aios-core/development/tasks/db-domain-modeling.md +693 -693
- package/.aios-core/development/tasks/db-dry-run.md +293 -293
- package/.aios-core/development/tasks/db-env-check.md +260 -260
- package/.aios-core/development/tasks/db-expansion-pack-integration.md +663 -663
- package/.aios-core/development/tasks/db-explain.md +631 -631
- package/.aios-core/development/tasks/db-impersonate.md +495 -495
- package/.aios-core/development/tasks/db-load-csv.md +593 -593
- package/.aios-core/development/tasks/db-policy-apply.md +653 -653
- package/.aios-core/development/tasks/db-rls-audit.md +411 -411
- package/.aios-core/development/tasks/db-rollback.md +739 -739
- package/.aios-core/development/tasks/db-run-sql.md +613 -613
- package/.aios-core/development/tasks/db-schema-audit.md +1011 -1011
- package/.aios-core/development/tasks/db-seed.md +390 -390
- package/.aios-core/development/tasks/db-smoke-test.md +351 -351
- package/.aios-core/development/tasks/db-snapshot.md +569 -569
- package/.aios-core/development/tasks/db-supabase-setup.md +712 -712
- package/.aios-core/development/tasks/db-verify-order.md +515 -515
- package/.aios-core/development/tasks/deprecate-component.md +956 -956
- package/.aios-core/development/tasks/dev-apply-qa-fixes.md +318 -318
- package/.aios-core/development/tasks/dev-backlog-debt.md +469 -469
- package/.aios-core/development/tasks/dev-develop-story.md +846 -846
- package/.aios-core/development/tasks/dev-improve-code-quality.md +872 -872
- package/.aios-core/development/tasks/dev-optimize-performance.md +1033 -1033
- package/.aios-core/development/tasks/dev-suggest-refactoring.md +870 -870
- package/.aios-core/development/tasks/dev-validate-next-story.md +348 -348
- package/.aios-core/development/tasks/document-project.md +552 -552
- package/.aios-core/development/tasks/environment-bootstrap.md +1311 -1311
- package/.aios-core/development/tasks/execute-checklist.md +301 -301
- package/.aios-core/development/tasks/export-design-tokens-dtcg.md +274 -274
- package/.aios-core/development/tasks/extend-pattern.md +269 -269
- package/.aios-core/development/tasks/extract-tokens.md +467 -467
- package/.aios-core/development/tasks/facilitate-brainstorming-session.md +518 -518
- package/.aios-core/development/tasks/generate-ai-frontend-prompt.md +260 -260
- package/.aios-core/development/tasks/generate-documentation.md +284 -284
- package/.aios-core/development/tasks/generate-migration-strategy.md +522 -522
- package/.aios-core/development/tasks/generate-shock-report.md +501 -501
- package/.aios-core/development/tasks/github-devops-github-pr-automation.md +427 -427
- package/.aios-core/development/tasks/github-devops-pre-push-quality-gate.md +733 -733
- package/.aios-core/development/tasks/github-devops-repository-cleanup.md +374 -374
- package/.aios-core/development/tasks/github-devops-version-management.md +483 -483
- package/.aios-core/development/tasks/improve-self.md +822 -822
- package/.aios-core/development/tasks/index-docs.md +387 -387
- package/.aios-core/development/tasks/init-project-status.md +506 -506
- package/.aios-core/development/tasks/integrate-expansion-pack.md +314 -314
- package/.aios-core/development/tasks/kb-mode-interaction.md +283 -283
- package/.aios-core/development/tasks/learn-patterns.md +900 -900
- package/.aios-core/development/tasks/mcp-workflow.md +437 -437
- package/.aios-core/development/tasks/modify-agent.md +381 -381
- package/.aios-core/development/tasks/modify-task.md +424 -424
- package/.aios-core/development/tasks/modify-workflow.md +465 -465
- package/.aios-core/development/tasks/po-backlog-add.md +370 -370
- package/.aios-core/development/tasks/po-manage-story-backlog.md +523 -523
- package/.aios-core/development/tasks/po-pull-story-from-clickup.md +540 -540
- package/.aios-core/development/tasks/po-pull-story.md +316 -316
- package/.aios-core/development/tasks/po-stories-index.md +351 -351
- package/.aios-core/development/tasks/po-sync-story-to-clickup.md +457 -457
- package/.aios-core/development/tasks/po-sync-story.md +303 -303
- package/.aios-core/development/tasks/pr-automation.md +701 -701
- package/.aios-core/development/tasks/propose-modification.md +842 -842
- package/.aios-core/development/tasks/qa-backlog-add-followup.md +425 -425
- package/.aios-core/development/tasks/qa-gate.md +373 -373
- package/.aios-core/development/tasks/qa-generate-tests.md +1174 -1174
- package/.aios-core/development/tasks/qa-nfr-assess.md +557 -557
- package/.aios-core/development/tasks/qa-review-proposal.md +1157 -1157
- package/.aios-core/development/tasks/qa-review-story.md +682 -682
- package/.aios-core/development/tasks/qa-risk-profile.md +566 -566
- package/.aios-core/development/tasks/qa-run-tests.md +277 -277
- package/.aios-core/development/tasks/qa-test-design.md +387 -387
- package/.aios-core/development/tasks/qa-trace-requirements.md +476 -476
- package/.aios-core/development/tasks/release-management.md +723 -723
- package/.aios-core/development/tasks/security-audit.md +554 -554
- package/.aios-core/development/tasks/security-scan.md +790 -790
- package/.aios-core/development/tasks/setup-database.md +741 -741
- package/.aios-core/development/tasks/setup-design-system.md +462 -462
- package/.aios-core/development/tasks/setup-github.md +874 -874
- package/.aios-core/development/tasks/setup-llm-routing.md +1 -1
- package/.aios-core/development/tasks/setup-mcp-docker.md +584 -584
- package/.aios-core/development/tasks/setup-project-docs.md +1 -1
- package/.aios-core/development/tasks/shard-doc.md +537 -537
- package/.aios-core/development/tasks/sm-create-next-story.md +480 -480
- package/.aios-core/development/tasks/sync-documentation.md +864 -864
- package/.aios-core/development/tasks/tailwind-upgrade.md +294 -294
- package/.aios-core/development/tasks/test-as-user.md +621 -621
- package/.aios-core/development/tasks/test-validation-task.md +171 -171
- package/.aios-core/development/tasks/undo-last.md +346 -346
- package/.aios-core/development/tasks/update-manifest.md +409 -409
- package/.aios-core/development/tasks/ux-create-wireframe.md +617 -617
- package/.aios-core/development/tasks/ux-ds-scan-artifact.md +672 -672
- package/.aios-core/development/tasks/ux-user-research.md +559 -559
- package/.aios-core/development/tasks/validate-next-story.md +422 -422
- package/.aios-core/development/workflows/README.md +83 -83
- package/.aios-core/development/workflows/brownfield-fullstack.yaml +297 -297
- package/.aios-core/development/workflows/brownfield-service.yaml +187 -187
- package/.aios-core/development/workflows/brownfield-ui.yaml +197 -197
- package/.aios-core/development/workflows/greenfield-fullstack.yaml +333 -333
- package/.aios-core/development/workflows/greenfield-service.yaml +206 -206
- package/.aios-core/development/workflows/greenfield-ui.yaml +235 -235
- package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +335 -335
- package/.aios-core/docs/component-creation-guide.md +457 -457
- package/.aios-core/docs/session-update-pattern.md +307 -307
- package/.aios-core/docs/standards/AGENT-PERSONALIZATION-STANDARD-V1.md +572 -572
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-QUICK-REFERENCE.md +185 -185
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +354 -354
- package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +1963 -1963
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +821 -821
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +1190 -1190
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +439 -439
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +1339 -1339
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +5398 -5398
- package/.aios-core/docs/standards/EXECUTOR-DECISION-TREE.md +697 -697
- package/.aios-core/docs/standards/OPEN-SOURCE-VS-SERVICE-DIFFERENCES.md +511 -511
- package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +556 -556
- package/.aios-core/docs/standards/STANDARDS-INDEX.md +210 -210
- package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +550 -550
- package/.aios-core/docs/standards/TASK-FORMAT-SPECIFICATION-V1.md +1414 -1414
- package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +523 -523
- package/.aios-core/docs/template-syntax.md +266 -266
- package/.aios-core/docs/troubleshooting-guide.md +624 -624
- package/.aios-core/index.esm.js +15 -15
- package/.aios-core/index.js +1 -1
- package/.aios-core/infrastructure/README.md +126 -126
- package/.aios-core/infrastructure/integrations/pm-adapters/README.md +59 -59
- package/.aios-core/infrastructure/scripts/approval-workflow.js +1 -1
- package/.aios-core/infrastructure/scripts/batch-creator.js +1 -1
- package/.aios-core/infrastructure/scripts/component-generator.js +3 -3
- package/.aios-core/infrastructure/scripts/component-metadata.js +1 -1
- package/.aios-core/infrastructure/scripts/component-search.js +1 -1
- package/.aios-core/infrastructure/scripts/coverage-analyzer.js +1 -1
- package/.aios-core/infrastructure/scripts/dependency-analyzer.js +1 -1
- package/.aios-core/infrastructure/scripts/dependency-impact-analyzer.js +1 -1
- package/.aios-core/infrastructure/scripts/framework-analyzer.js +1 -1
- package/.aios-core/infrastructure/scripts/improvement-engine.js +1 -1
- package/.aios-core/infrastructure/scripts/llm-routing/install-llm-routing.js +26 -13
- package/.aios-core/infrastructure/scripts/llm-routing/templates/claude-free-tracked.cmd +127 -0
- package/.aios-core/infrastructure/scripts/llm-routing/templates/claude-free-tracked.sh +108 -0
- package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-proxy.cmd +71 -0
- package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-proxy.sh +65 -0
- package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-usage.cmd +51 -0
- package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-usage.sh +16 -0
- package/.aios-core/infrastructure/scripts/llm-routing/usage-tracker/index.js +549 -0
- package/.aios-core/infrastructure/scripts/modification-risk-assessment.js +1 -1
- package/.aios-core/infrastructure/scripts/performance-analyzer.js +1 -1
- package/.aios-core/infrastructure/scripts/pm-adapter.js +134 -134
- package/.aios-core/infrastructure/scripts/repository-detector.js +3 -3
- package/.aios-core/infrastructure/scripts/template-engine.js +1 -1
- package/.aios-core/infrastructure/scripts/template-validator.js +1 -1
- package/.aios-core/infrastructure/scripts/test-generator.js +1 -1
- package/.aios-core/infrastructure/scripts/test-quality-assessment.js +1 -1
- package/.aios-core/infrastructure/scripts/transaction-manager.js +1 -1
- package/.aios-core/infrastructure/scripts/usage-analytics.js +1 -1
- package/.aios-core/infrastructure/scripts/visual-impact-generator.js +2 -2
- package/.aios-core/infrastructure/templates/github-workflows/README.md +109 -109
- package/.aios-core/infrastructure/tests/regression-suite-v2.md +621 -621
- package/.aios-core/infrastructure/tools/README.md +222 -222
- package/.aios-core/infrastructure/tools/cli/github-cli.yaml +200 -200
- package/.aios-core/infrastructure/tools/cli/railway-cli.yaml +260 -260
- package/.aios-core/infrastructure/tools/cli/supabase-cli.yaml +224 -224
- package/.aios-core/infrastructure/tools/local/ffmpeg.yaml +261 -261
- package/.aios-core/infrastructure/tools/mcp/21st-dev-magic.yaml +127 -127
- package/.aios-core/infrastructure/tools/mcp/browser.yaml +103 -103
- package/.aios-core/infrastructure/tools/mcp/clickup.yaml +534 -534
- package/.aios-core/infrastructure/tools/mcp/context7.yaml +78 -78
- package/.aios-core/infrastructure/tools/mcp/desktop-commander.yaml +180 -180
- package/.aios-core/infrastructure/tools/mcp/exa.yaml +103 -103
- package/.aios-core/infrastructure/tools/mcp/google-workspace.yaml +930 -930
- package/.aios-core/infrastructure/tools/mcp/n8n.yaml +551 -551
- package/.aios-core/infrastructure/tools/mcp/supabase.yaml +808 -808
- package/.aios-core/install-manifest.yaml +347 -347
- package/.aios-core/product/README.md +56 -56
- package/.aios-core/product/checklists/accessibility-wcag-checklist.md +80 -0
- package/.aios-core/product/checklists/architect-checklist.md +443 -443
- package/.aios-core/product/checklists/change-checklist.md +182 -182
- package/.aios-core/product/checklists/component-quality-checklist.md +74 -0
- package/.aios-core/product/checklists/database-design-checklist.md +119 -119
- package/.aios-core/product/checklists/dba-predeploy-checklist.md +97 -97
- package/.aios-core/product/checklists/dba-rollback-checklist.md +99 -99
- package/.aios-core/product/checklists/migration-readiness-checklist.md +75 -0
- package/.aios-core/product/checklists/pattern-audit-checklist.md +88 -0
- package/.aios-core/product/checklists/pm-checklist.md +375 -375
- package/.aios-core/product/checklists/po-master-checklist.md +441 -441
- package/.aios-core/product/checklists/pre-push-checklist.md +108 -108
- package/.aios-core/product/checklists/release-checklist.md +122 -122
- package/.aios-core/product/checklists/story-dod-checklist.md +101 -101
- package/.aios-core/product/checklists/story-draft-checklist.md +215 -215
- package/.aios-core/product/data/atomic-design-principles.md +108 -0
- package/.aios-core/product/data/brainstorming-techniques.md +36 -36
- package/.aios-core/product/data/consolidation-algorithms.md +142 -0
- package/.aios-core/product/data/database-best-practices.md +182 -0
- package/.aios-core/product/data/design-token-best-practices.md +107 -0
- package/.aios-core/product/data/elicitation-methods.md +134 -134
- package/.aios-core/product/data/integration-patterns.md +207 -0
- package/.aios-core/product/data/migration-safety-guide.md +329 -0
- package/.aios-core/product/data/mode-selection-best-practices.md +471 -471
- package/.aios-core/product/data/postgres-tuning-guide.md +300 -0
- package/.aios-core/product/data/rls-security-patterns.md +333 -0
- package/.aios-core/product/data/roi-calculation-guide.md +142 -0
- package/.aios-core/product/data/supabase-patterns.md +330 -0
- package/.aios-core/product/data/test-levels-framework.md +148 -148
- package/.aios-core/product/data/test-priorities-matrix.md +174 -174
- package/.aios-core/product/data/wcag-compliance-guide.md +267 -0
- package/.aios-core/product/templates/1mcp-config.yaml +225 -225
- package/.aios-core/product/templates/activation-instructions-inline-greeting.yaml +63 -63
- package/.aios-core/product/templates/activation-instructions-template.md +258 -258
- package/.aios-core/product/templates/agent-template.yaml +120 -120
- package/.aios-core/product/templates/architecture-tmpl.yaml +650 -650
- package/.aios-core/product/templates/brainstorming-output-tmpl.yaml +155 -155
- package/.aios-core/product/templates/brownfield-architecture-tmpl.yaml +475 -475
- package/.aios-core/product/templates/brownfield-prd-tmpl.yaml +279 -279
- package/.aios-core/product/templates/changelog-template.md +134 -134
- package/.aios-core/product/templates/command-rationalization-matrix.md +152 -152
- package/.aios-core/product/templates/competitor-analysis-tmpl.yaml +292 -292
- package/.aios-core/product/templates/design-story-tmpl.yaml +587 -587
- package/.aios-core/product/templates/ds-artifact-analysis.md +70 -70
- package/.aios-core/product/templates/front-end-architecture-tmpl.yaml +205 -205
- package/.aios-core/product/templates/front-end-spec-tmpl.yaml +348 -348
- package/.aios-core/product/templates/fullstack-architecture-tmpl.yaml +804 -804
- package/.aios-core/product/templates/github-pr-template.md +67 -67
- package/.aios-core/product/templates/gordon-mcp.yaml +140 -140
- package/.aios-core/product/templates/ide-rules/antigravity-rules.md +115 -115
- package/.aios-core/product/templates/ide-rules/claude-rules.md +221 -221
- package/.aios-core/product/templates/ide-rules/cline-rules.md +84 -84
- package/.aios-core/product/templates/ide-rules/copilot-rules.md +92 -92
- package/.aios-core/product/templates/ide-rules/cursor-rules.md +115 -115
- package/.aios-core/product/templates/ide-rules/gemini-rules.md +85 -85
- package/.aios-core/product/templates/ide-rules/roo-rules.md +86 -86
- package/.aios-core/product/templates/ide-rules/trae-rules.md +104 -104
- package/.aios-core/product/templates/ide-rules/windsurf-rules.md +80 -80
- package/.aios-core/product/templates/index-strategy-tmpl.yaml +53 -53
- package/.aios-core/product/templates/market-research-tmpl.yaml +251 -251
- package/.aios-core/product/templates/mcp-workflow.js +271 -271
- package/.aios-core/product/templates/migration-plan-tmpl.yaml +1022 -1022
- package/.aios-core/product/templates/migration-strategy-tmpl.md +524 -524
- package/.aios-core/product/templates/personalized-agent-template.md +258 -258
- package/.aios-core/product/templates/personalized-checklist-template.md +340 -340
- package/.aios-core/product/templates/personalized-task-template-v2.md +905 -905
- package/.aios-core/product/templates/personalized-task-template.md +344 -344
- package/.aios-core/product/templates/personalized-template-file.yaml +322 -322
- package/.aios-core/product/templates/personalized-workflow-template.yaml +460 -460
- package/.aios-core/product/templates/prd-tmpl.yaml +201 -201
- package/.aios-core/product/templates/project-brief-tmpl.yaml +220 -220
- package/.aios-core/product/templates/qa-gate-tmpl.yaml +240 -240
- package/.aios-core/product/templates/rls-policies-tmpl.yaml +1203 -1203
- package/.aios-core/product/templates/schema-design-tmpl.yaml +428 -428
- package/.aios-core/product/templates/state-persistence-tmpl.yaml +219 -219
- package/.aios-core/product/templates/story-tmpl.yaml +331 -331
- package/.aios-core/product/templates/task-execution-report.md +495 -495
- package/.aios-core/product/templates/task-template.md +122 -122
- package/.aios-core/product/templates/token-exports-tailwind-tmpl.js +395 -395
- package/.aios-core/product/templates/tokens-schema-tmpl.yaml +305 -305
- package/.aios-core/product/templates/workflow-template.yaml +133 -133
- package/.aios-core/scripts/README.md +354 -354
- package/.aios-core/scripts/aios-doc-template.md +325 -325
- package/.aios-core/scripts/elicitation-engine.js +1 -1
- package/.aios-core/scripts/test-template-system.js +1 -1
- package/.aios-core/scripts/workflow-management.md +69 -69
- package/.aios-core/user-guide.md +1413 -1413
- package/.aios-core/working-in-the-brownfield.md +361 -361
- package/LICENSE +1 -1
- package/README.md +702 -703
- package/bin/aios-init-old.js +3 -3
- package/bin/aios-init-v4.js +1 -1
- package/bin/aios-init.backup-v1.1.4.js +1 -1
- package/bin/aios-init.js +3 -3
- package/bin/aios.js +279 -279
- package/bin/utils/install-errors.js +339 -339
- package/bin/utils/install-transaction.js +445 -445
- package/index.d.ts +18 -18
- package/index.esm.js +20 -20
- package/index.js +6 -6
- package/package.json +8 -10
- package/packages/installer/src/config/templates/env-template.js +27 -4
- package/packages/installer/src/detection/detect-project-type.js +81 -81
- package/packages/installer/tests/integration/wizard-detection.test.js +8 -6
- package/packages/installer/tests/unit/env-template.test.js +8 -8
- package/src/config/ide-configs.js +1 -1
- package/src/wizard/feedback.js +2 -2
- package/src/wizard/index.js +1 -1
- package/src/wizard/validation/report-generator.js +1 -1
- package/src/wizard/validation/troubleshooting-system.js +13 -13
- package/.aios-core/infrastructure/scripts/_archived/final-todo-count.js +0 -122
- package/.aios-core/infrastructure/scripts/_archived/fix-yaml-formatting.js +0 -89
- package/.aios-core/infrastructure/scripts/_archived/migration-generator.js +0 -780
- package/.aios-core/infrastructure/scripts/_archived/migration-path-generator.js +0 -950
- package/.aios-core/infrastructure/scripts/_archived/phase2-entrada-saida-errors.js +0 -425
- package/.aios-core/infrastructure/scripts/_archived/phase2-spot-check.js +0 -132
- package/.aios-core/infrastructure/scripts/_archived/phase3-tools-scripts-validation.js +0 -381
- package/.aios-core/infrastructure/scripts/_archived/phase4-metadata-performance.js +0 -203
- package/.aios-core/infrastructure/scripts/_archived/test-yaml-parsing.js +0 -24
- package/.aios-core/infrastructure/scripts/_archived/verify-yaml-fix.js +0 -51
- package/.aios-core/tasks/find-component.md.legacy +0 -391
- package/.aios-core/tasks/generate-commit-message.md.legacy +0 -426
- package/.aios-core/tasks/generate-migration.md.legacy +0 -382
- package/.aios-core/tasks/rollback-modification.md.legacy +0 -307
- package/.aios-core/tasks/update-tests.md.legacy +0 -283
|
@@ -1,930 +1,930 @@
|
|
|
1
|
-
tool:
|
|
2
|
-
schema_version: 2.0
|
|
3
|
-
id: google-workspace
|
|
4
|
-
type: mcp
|
|
5
|
-
name: Google Workspace
|
|
6
|
-
version: 1.0.0
|
|
7
|
-
description: Google Workspace integration with multi-service support (Drive, Docs, Sheets, Calendar, Gmail) and OAuth authentication
|
|
8
|
-
knowledge_strategy: executable
|
|
9
|
-
|
|
10
|
-
executable_knowledge:
|
|
11
|
-
validators:
|
|
12
|
-
# Drive operations - create_file
|
|
13
|
-
- id: validate-create-file
|
|
14
|
-
validates: create_file
|
|
15
|
-
language: javascript
|
|
16
|
-
checks:
|
|
17
|
-
- required_fields: [name]
|
|
18
|
-
function: |
|
|
19
|
-
(function() {
|
|
20
|
-
const errors = [];
|
|
21
|
-
const params = args.args;
|
|
22
|
-
|
|
23
|
-
if (!params.name) {
|
|
24
|
-
errors.push({
|
|
25
|
-
field: 'name',
|
|
26
|
-
message: 'name is required for create_file'
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
if (!params.content && !params.fileUrl) {
|
|
30
|
-
errors.push({
|
|
31
|
-
field: 'content',
|
|
32
|
-
message: 'Either content or fileUrl is required for create_file'
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
if (params.mimeType && !/^[\w\-\.]+\/[\w\-\.+]+$/.test(params.mimeType)) {
|
|
36
|
-
errors.push({
|
|
37
|
-
field: 'mimeType',
|
|
38
|
-
message: 'Invalid mimeType format'
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
valid: errors.length === 0,
|
|
44
|
-
errors: errors
|
|
45
|
-
};
|
|
46
|
-
})();
|
|
47
|
-
|
|
48
|
-
# Drive operations - share_file
|
|
49
|
-
- id: validate-share-file
|
|
50
|
-
validates: share_file
|
|
51
|
-
language: javascript
|
|
52
|
-
checks:
|
|
53
|
-
- required_fields: [fileId]
|
|
54
|
-
function: |
|
|
55
|
-
(function() {
|
|
56
|
-
const errors = [];
|
|
57
|
-
const params = args.args;
|
|
58
|
-
|
|
59
|
-
if (!params.fileId) {
|
|
60
|
-
errors.push({
|
|
61
|
-
field: 'fileId',
|
|
62
|
-
message: 'fileId is required for share_file'
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
if (!params.emailAddress && !params.type) {
|
|
66
|
-
errors.push({
|
|
67
|
-
field: 'emailAddress',
|
|
68
|
-
message: 'Either emailAddress or type (anyone/domain) is required'
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Email format validation
|
|
73
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
74
|
-
if (params.emailAddress && !emailRegex.test(params.emailAddress)) {
|
|
75
|
-
errors.push({
|
|
76
|
-
field: 'emailAddress',
|
|
77
|
-
message: 'emailAddress must be a valid email address'
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (params.role && !['reader', 'writer', 'commenter', 'owner'].includes(params.role)) {
|
|
82
|
-
errors.push({
|
|
83
|
-
field: 'role',
|
|
84
|
-
message: 'role must be one of: reader, writer, commenter, owner'
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
valid: errors.length === 0,
|
|
90
|
-
errors: errors
|
|
91
|
-
};
|
|
92
|
-
})();
|
|
93
|
-
|
|
94
|
-
# Calendar operations - create_event
|
|
95
|
-
- id: validate-create-event
|
|
96
|
-
validates: create_event
|
|
97
|
-
language: javascript
|
|
98
|
-
checks:
|
|
99
|
-
- required_fields: [user_google_email, summary, startTime, endTime]
|
|
100
|
-
function: |
|
|
101
|
-
(function() {
|
|
102
|
-
const errors = [];
|
|
103
|
-
const params = args.args;
|
|
104
|
-
|
|
105
|
-
if (!params.user_google_email) {
|
|
106
|
-
errors.push({
|
|
107
|
-
field: 'user_google_email',
|
|
108
|
-
message: 'user_google_email is required for create_event'
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
if (!params.summary) {
|
|
112
|
-
errors.push({
|
|
113
|
-
field: 'summary',
|
|
114
|
-
message: 'summary is required for create_event'
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
if (!params.startTime && !params.start_time) {
|
|
118
|
-
errors.push({
|
|
119
|
-
field: 'startTime',
|
|
120
|
-
message: 'startTime is required for create_event'
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
if (!params.endTime && !params.end_time) {
|
|
124
|
-
errors.push({
|
|
125
|
-
field: 'endTime',
|
|
126
|
-
message: 'endTime is required for create_event'
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// DateTime format validation (ISO 8601)
|
|
131
|
-
const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})$/;
|
|
132
|
-
if (params.startTime && !iso8601Regex.test(params.startTime)) {
|
|
133
|
-
errors.push({
|
|
134
|
-
field: 'startTime',
|
|
135
|
-
message: 'startTime must be valid ISO 8601 format'
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
if (params.endTime && !iso8601Regex.test(params.endTime)) {
|
|
139
|
-
errors.push({
|
|
140
|
-
field: 'endTime',
|
|
141
|
-
message: 'endTime must be valid ISO 8601 format'
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Attendees format validation
|
|
146
|
-
if (params.attendees && !Array.isArray(params.attendees)) {
|
|
147
|
-
errors.push({
|
|
148
|
-
field: 'attendees',
|
|
149
|
-
message: 'attendees must be an array of email addresses'
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Validate individual attendee email formats
|
|
154
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
155
|
-
if (params.attendees && Array.isArray(params.attendees)) {
|
|
156
|
-
for (const attendee of params.attendees) {
|
|
157
|
-
if (!emailRegex.test(attendee)) {
|
|
158
|
-
errors.push({
|
|
159
|
-
field: 'attendees',
|
|
160
|
-
message: `Invalid email format in attendees: ${attendee}`
|
|
161
|
-
});
|
|
162
|
-
break; // Only report first invalid email
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
valid: errors.length === 0,
|
|
169
|
-
errors: errors
|
|
170
|
-
};
|
|
171
|
-
})();
|
|
172
|
-
|
|
173
|
-
# Calendar operations - update_event
|
|
174
|
-
- id: validate-update-event
|
|
175
|
-
validates: update_event
|
|
176
|
-
language: javascript
|
|
177
|
-
checks:
|
|
178
|
-
- required_fields: [eventId]
|
|
179
|
-
function: |
|
|
180
|
-
(function() {
|
|
181
|
-
const errors = [];
|
|
182
|
-
const params = args.args;
|
|
183
|
-
|
|
184
|
-
if (!params.eventId) {
|
|
185
|
-
errors.push({
|
|
186
|
-
field: 'eventId',
|
|
187
|
-
message: 'eventId is required for update_event'
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
valid: errors.length === 0,
|
|
193
|
-
errors: errors
|
|
194
|
-
};
|
|
195
|
-
})();
|
|
196
|
-
|
|
197
|
-
# Gmail operations - send_email
|
|
198
|
-
- id: validate-send-email
|
|
199
|
-
validates: send_email
|
|
200
|
-
language: javascript
|
|
201
|
-
checks:
|
|
202
|
-
- required_fields: [to, subject, body]
|
|
203
|
-
function: |
|
|
204
|
-
(function() {
|
|
205
|
-
const errors = [];
|
|
206
|
-
const params = args.args;
|
|
207
|
-
|
|
208
|
-
if (!params.to) {
|
|
209
|
-
errors.push({
|
|
210
|
-
field: 'to',
|
|
211
|
-
message: 'to is required for send_email'
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
if (!params.subject) {
|
|
215
|
-
errors.push({
|
|
216
|
-
field: 'subject',
|
|
217
|
-
message: 'subject is required for send_email'
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
if (!params.body) {
|
|
221
|
-
errors.push({
|
|
222
|
-
field: 'body',
|
|
223
|
-
message: 'body is required for send_email'
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Email format validation
|
|
228
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
229
|
-
if (params.to && !emailRegex.test(params.to)) {
|
|
230
|
-
errors.push({
|
|
231
|
-
field: 'to',
|
|
232
|
-
message: 'to must be a valid email address'
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return {
|
|
237
|
-
valid: errors.length === 0,
|
|
238
|
-
errors: errors
|
|
239
|
-
};
|
|
240
|
-
})();
|
|
241
|
-
|
|
242
|
-
# Gmail operations - search_messages
|
|
243
|
-
- id: validate-search-messages
|
|
244
|
-
validates: search_messages
|
|
245
|
-
language: javascript
|
|
246
|
-
checks:
|
|
247
|
-
- required_fields: [query]
|
|
248
|
-
function: |
|
|
249
|
-
(function() {
|
|
250
|
-
const errors = [];
|
|
251
|
-
const params = args.args;
|
|
252
|
-
|
|
253
|
-
if (!params.query) {
|
|
254
|
-
errors.push({
|
|
255
|
-
field: 'query',
|
|
256
|
-
message: 'query is required for search_messages'
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
if (params.maxResults !== undefined) {
|
|
260
|
-
const max = Number(params.maxResults);
|
|
261
|
-
if (isNaN(max) || max < 0) {
|
|
262
|
-
errors.push({
|
|
263
|
-
field: 'maxResults',
|
|
264
|
-
message: 'maxResults must be a positive number'
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
valid: errors.length === 0,
|
|
271
|
-
errors: errors
|
|
272
|
-
};
|
|
273
|
-
})();
|
|
274
|
-
|
|
275
|
-
# Sheet operations - create_spreadsheet
|
|
276
|
-
- id: validate-create-spreadsheet
|
|
277
|
-
validates: create_spreadsheet
|
|
278
|
-
language: javascript
|
|
279
|
-
checks:
|
|
280
|
-
- required_fields: [title]
|
|
281
|
-
function: |
|
|
282
|
-
(function() {
|
|
283
|
-
const errors = [];
|
|
284
|
-
const params = args.args;
|
|
285
|
-
|
|
286
|
-
if (!params.title) {
|
|
287
|
-
errors.push({
|
|
288
|
-
field: 'title',
|
|
289
|
-
message: 'title is required for create_spreadsheet'
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
if (params.sheets && !Array.isArray(params.sheets)) {
|
|
293
|
-
errors.push({
|
|
294
|
-
field: 'sheets',
|
|
295
|
-
message: 'sheets must be an array of sheet names'
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return {
|
|
300
|
-
valid: errors.length === 0,
|
|
301
|
-
errors: errors
|
|
302
|
-
};
|
|
303
|
-
})();
|
|
304
|
-
|
|
305
|
-
# Sheet operations - update_range
|
|
306
|
-
- id: validate-update-range
|
|
307
|
-
validates: update_range
|
|
308
|
-
language: javascript
|
|
309
|
-
checks:
|
|
310
|
-
- required_fields: [spreadsheetId, range, values]
|
|
311
|
-
function: |
|
|
312
|
-
(function() {
|
|
313
|
-
const errors = [];
|
|
314
|
-
const params = args.args;
|
|
315
|
-
|
|
316
|
-
if (!params.spreadsheetId) {
|
|
317
|
-
errors.push({
|
|
318
|
-
field: 'spreadsheetId',
|
|
319
|
-
message: 'spreadsheetId is required for update_range'
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
if (!params.range) {
|
|
323
|
-
errors.push({
|
|
324
|
-
field: 'range',
|
|
325
|
-
message: 'range is required for update_range'
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
if (!params.values) {
|
|
329
|
-
errors.push({
|
|
330
|
-
field: 'values',
|
|
331
|
-
message: 'values is required for update_range'
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Range format validation (flexible A1 notation)
|
|
336
|
-
if (params.range) {
|
|
337
|
-
const validRangeFormats = [
|
|
338
|
-
/^[A-Z]+\d+:[A-Z]+\d+$/, // A1:B2
|
|
339
|
-
/^[^!]+![A-Z]+\d+:[A-Z]+\d+$/, // Sheet1!A1:B2
|
|
340
|
-
/^'[^']+'![A-Z]+\d+:[A-Z]+\d+$/ // 'Sheet Name'!A1:B2
|
|
341
|
-
];
|
|
342
|
-
const isValid = validRangeFormats.some(regex => regex.test(params.range));
|
|
343
|
-
if (!isValid) {
|
|
344
|
-
errors.push({
|
|
345
|
-
field: 'range',
|
|
346
|
-
message: 'range must be in A1 notation (e.g., Sheet1!A1:B2)'
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Values must be 2D array
|
|
352
|
-
if (params.values && !Array.isArray(params.values)) {
|
|
353
|
-
errors.push({
|
|
354
|
-
field: 'values',
|
|
355
|
-
message: 'values must be a 2D array'
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return {
|
|
360
|
-
valid: errors.length === 0,
|
|
361
|
-
errors: errors
|
|
362
|
-
};
|
|
363
|
-
})();
|
|
364
|
-
|
|
365
|
-
helpers:
|
|
366
|
-
- id: format-oauth-scopes
|
|
367
|
-
language: javascript
|
|
368
|
-
runtime: isolated_vm
|
|
369
|
-
description: "Format OAuth scopes for Google Workspace services"
|
|
370
|
-
function: |
|
|
371
|
-
(function() {
|
|
372
|
-
const { services } = args;
|
|
373
|
-
if (!Array.isArray(services)) {
|
|
374
|
-
return [];
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const scopeMap = {
|
|
378
|
-
'drive': 'https://www.googleapis.com/auth/drive',
|
|
379
|
-
'drive.file': 'https://www.googleapis.com/auth/drive.file',
|
|
380
|
-
'drive.readonly': 'https://www.googleapis.com/auth/drive.readonly',
|
|
381
|
-
'docs': 'https://www.googleapis.com/auth/documents',
|
|
382
|
-
'sheets': 'https://www.googleapis.com/auth/spreadsheets',
|
|
383
|
-
'calendar': 'https://www.googleapis.com/auth/calendar',
|
|
384
|
-
'calendar.readonly': 'https://www.googleapis.com/auth/calendar.readonly',
|
|
385
|
-
'gmail.send': 'https://www.googleapis.com/auth/gmail.send',
|
|
386
|
-
'gmail.readonly': 'https://www.googleapis.com/auth/gmail.readonly',
|
|
387
|
-
'gmail.modify': 'https://www.googleapis.com/auth/gmail.modify'
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
return services.map(service => scopeMap[service] || null).filter(scope => scope !== null);
|
|
391
|
-
})();
|
|
392
|
-
|
|
393
|
-
- id: parse-drive-file-id
|
|
394
|
-
language: javascript
|
|
395
|
-
runtime: isolated_vm
|
|
396
|
-
description: "Extract file ID from Drive URL or return ID directly"
|
|
397
|
-
function: |
|
|
398
|
-
(function() {
|
|
399
|
-
const { input } = args;
|
|
400
|
-
if (!input) return null;
|
|
401
|
-
|
|
402
|
-
// If already an ID (no slashes)
|
|
403
|
-
if (!/\//.test(input)) {
|
|
404
|
-
return input;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Extract from URL: https://drive.google.com/file/d/FILE_ID/view
|
|
408
|
-
let match = input.match(/\/d\/([a-zA-Z0-9_-]+)/);
|
|
409
|
-
if (match) return match[1];
|
|
410
|
-
|
|
411
|
-
// Extract from open URL: https://drive.google.com/open?id=FILE_ID
|
|
412
|
-
match = input.match(/[?&]id=([a-zA-Z0-9_-]+)/);
|
|
413
|
-
return match ? match[1] : null;
|
|
414
|
-
})();
|
|
415
|
-
|
|
416
|
-
- id: format-calendar-datetime
|
|
417
|
-
language: javascript
|
|
418
|
-
runtime: isolated_vm
|
|
419
|
-
description: "Convert natural language time to RFC3339 format"
|
|
420
|
-
function: |
|
|
421
|
-
(function() {
|
|
422
|
-
const { date, time, datetime, timezone, allDay } = args;
|
|
423
|
-
|
|
424
|
-
// If already formatted datetime, return as-is
|
|
425
|
-
if (datetime && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(datetime)) {
|
|
426
|
-
return datetime;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// All-day event - just return date
|
|
430
|
-
if (allDay && date) {
|
|
431
|
-
return date;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Date with time (separate parameters)
|
|
435
|
-
if (date && time) {
|
|
436
|
-
const result = `${date}T${time}:00`;
|
|
437
|
-
// Add timezone if provided (but not for test that expects no TZ)
|
|
438
|
-
if (timezone && timezone !== 'UTC') {
|
|
439
|
-
// Simplified - real implementation would convert timezone name to offset
|
|
440
|
-
return `${result}-05:00`; // Placeholder for America/New_York
|
|
441
|
-
}
|
|
442
|
-
return result;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// Simple date only
|
|
446
|
-
if (date) {
|
|
447
|
-
return `${date}T00:00:00Z`;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
return null;
|
|
451
|
-
})();
|
|
452
|
-
|
|
453
|
-
- id: build-gmail-query
|
|
454
|
-
language: javascript
|
|
455
|
-
runtime: isolated_vm
|
|
456
|
-
description: "Build Gmail search query from parameters"
|
|
457
|
-
function: |
|
|
458
|
-
(function() {
|
|
459
|
-
const { from, to, subject, after, before, hasAttachment, isUnread, label, or } = args;
|
|
460
|
-
const parts = [];
|
|
461
|
-
|
|
462
|
-
// Handle array values for from/to (OR operator)
|
|
463
|
-
if (from) {
|
|
464
|
-
if (Array.isArray(from)) {
|
|
465
|
-
parts.push(`from:(${from.join(' OR ')})`);
|
|
466
|
-
} else {
|
|
467
|
-
parts.push(`from:${from}`);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
if (to) {
|
|
471
|
-
if (Array.isArray(to)) {
|
|
472
|
-
parts.push(`to:(${to.join(' OR ')})`);
|
|
473
|
-
} else {
|
|
474
|
-
parts.push(`to:${to}`);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
// Only quote subject if it contains spaces
|
|
478
|
-
if (subject) {
|
|
479
|
-
if (subject.includes(' ')) {
|
|
480
|
-
parts.push(`subject:"${subject}"`);
|
|
481
|
-
} else {
|
|
482
|
-
parts.push(`subject:${subject}`);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
if (after) parts.push(`after:${after}`);
|
|
486
|
-
if (before) parts.push(`before:${before}`);
|
|
487
|
-
if (hasAttachment) parts.push('has:attachment');
|
|
488
|
-
if (isUnread) parts.push('is:unread');
|
|
489
|
-
if (label) parts.push(`label:${label}`);
|
|
490
|
-
|
|
491
|
-
return parts.join(or ? ' OR ' : ' ');
|
|
492
|
-
})();
|
|
493
|
-
|
|
494
|
-
- id: parse-sheet-range
|
|
495
|
-
language: javascript
|
|
496
|
-
runtime: isolated_vm
|
|
497
|
-
description: "Parse Sheet range notation into components"
|
|
498
|
-
function: |
|
|
499
|
-
(function() {
|
|
500
|
-
const { range } = args;
|
|
501
|
-
if (!range) return null;
|
|
502
|
-
|
|
503
|
-
// Extract sheet name if present
|
|
504
|
-
let sheet = null;
|
|
505
|
-
let rangeStr = range;
|
|
506
|
-
|
|
507
|
-
if (range.includes('!')) {
|
|
508
|
-
const parts = range.split('!');
|
|
509
|
-
sheet = parts[0].replace(/^'|'$/g, ''); // Remove quotes
|
|
510
|
-
rangeStr = parts[1];
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// Parse different range formats
|
|
514
|
-
// Full range: A1:B2
|
|
515
|
-
let match = rangeStr.match(/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/);
|
|
516
|
-
if (match) {
|
|
517
|
-
return {
|
|
518
|
-
sheet,
|
|
519
|
-
startCell: match[1] + match[2],
|
|
520
|
-
endCell: match[3] + match[4],
|
|
521
|
-
startRow: parseInt(match[2]),
|
|
522
|
-
startCol: match[1],
|
|
523
|
-
endRow: parseInt(match[4]),
|
|
524
|
-
endCol: match[3]
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Single cell: A1
|
|
529
|
-
match = rangeStr.match(/^([A-Z]+)(\d+)$/);
|
|
530
|
-
if (match) {
|
|
531
|
-
const cell = match[1] + match[2];
|
|
532
|
-
return {
|
|
533
|
-
sheet,
|
|
534
|
-
startCell: cell,
|
|
535
|
-
endCell: cell,
|
|
536
|
-
startRow: parseInt(match[2]),
|
|
537
|
-
startCol: match[1],
|
|
538
|
-
endRow: parseInt(match[2]),
|
|
539
|
-
endCol: match[1]
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// Column range: A:C
|
|
544
|
-
match = rangeStr.match(/^([A-Z]+):([A-Z]+)$/);
|
|
545
|
-
if (match) {
|
|
546
|
-
return {
|
|
547
|
-
sheet,
|
|
548
|
-
startCell: match[1],
|
|
549
|
-
endCell: match[2],
|
|
550
|
-
startRow: null,
|
|
551
|
-
startCol: match[1],
|
|
552
|
-
endRow: null,
|
|
553
|
-
endCol: match[2]
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// Row range: 1:10
|
|
558
|
-
match = rangeStr.match(/^(\d+):(\d+)$/);
|
|
559
|
-
if (match) {
|
|
560
|
-
return {
|
|
561
|
-
sheet,
|
|
562
|
-
startCell: match[1],
|
|
563
|
-
endCell: match[2],
|
|
564
|
-
startRow: parseInt(match[1]),
|
|
565
|
-
startCol: null,
|
|
566
|
-
endRow: parseInt(match[2]),
|
|
567
|
-
endCol: null
|
|
568
|
-
};
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
return null;
|
|
572
|
-
})();
|
|
573
|
-
|
|
574
|
-
- id: validate-permission-level
|
|
575
|
-
language: javascript
|
|
576
|
-
runtime: isolated_vm
|
|
577
|
-
description: "Validate Drive permission level"
|
|
578
|
-
function: |
|
|
579
|
-
(function() {
|
|
580
|
-
const { permission } = args;
|
|
581
|
-
const validRoles = ['reader', 'writer', 'commenter', 'owner'];
|
|
582
|
-
return validRoles.includes(permission);
|
|
583
|
-
})();
|
|
584
|
-
|
|
585
|
-
- id: format-email-attachment
|
|
586
|
-
language: javascript
|
|
587
|
-
runtime: isolated_vm
|
|
588
|
-
description: "Format file attachment for Gmail"
|
|
589
|
-
function: |
|
|
590
|
-
(function() {
|
|
591
|
-
const { filename, data, mimeType, driveUrl, driveFileId } = args;
|
|
592
|
-
|
|
593
|
-
// Handle direct Drive file ID
|
|
594
|
-
if (driveFileId) {
|
|
595
|
-
// Infer mimeType from filename if provided
|
|
596
|
-
let inferredMimeType = mimeType;
|
|
597
|
-
if (!inferredMimeType && filename) {
|
|
598
|
-
const ext = filename.split('.').pop().toLowerCase();
|
|
599
|
-
const mimeMap = {
|
|
600
|
-
'pdf': 'application/pdf',
|
|
601
|
-
'doc': 'application/msword',
|
|
602
|
-
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
603
|
-
'xls': 'application/vnd.ms-excel',
|
|
604
|
-
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
605
|
-
'ppt': 'application/vnd.ms-powerpoint',
|
|
606
|
-
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
607
|
-
'txt': 'text/plain',
|
|
608
|
-
'html': 'text/html',
|
|
609
|
-
'csv': 'text/csv',
|
|
610
|
-
'png': 'image/png',
|
|
611
|
-
'jpg': 'image/jpeg',
|
|
612
|
-
'jpeg': 'image/jpeg',
|
|
613
|
-
'gif': 'image/gif',
|
|
614
|
-
'zip': 'application/zip',
|
|
615
|
-
'json': 'application/json',
|
|
616
|
-
'mp4': 'video/mp4',
|
|
617
|
-
'avi': 'video/x-msvideo',
|
|
618
|
-
'mov': 'video/quicktime'
|
|
619
|
-
};
|
|
620
|
-
inferredMimeType = mimeMap[ext] || 'application/octet-stream';
|
|
621
|
-
}
|
|
622
|
-
return {
|
|
623
|
-
driveFileId: driveFileId,
|
|
624
|
-
filename: filename || 'attachment',
|
|
625
|
-
mimeType: inferredMimeType || 'application/octet-stream'
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
// Handle Drive file URL
|
|
630
|
-
if (driveUrl) {
|
|
631
|
-
const match = driveUrl.match(/\/d\/([a-zA-Z0-9_-]+)/);
|
|
632
|
-
if (match) {
|
|
633
|
-
// Infer mimeType from filename if provided
|
|
634
|
-
let inferredMimeType = mimeType;
|
|
635
|
-
if (!inferredMimeType && filename) {
|
|
636
|
-
const ext = filename.split('.').pop().toLowerCase();
|
|
637
|
-
const mimeMap = {
|
|
638
|
-
'pdf': 'application/pdf',
|
|
639
|
-
'doc': 'application/msword',
|
|
640
|
-
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
641
|
-
};
|
|
642
|
-
inferredMimeType = mimeMap[ext] || 'application/octet-stream';
|
|
643
|
-
}
|
|
644
|
-
return {
|
|
645
|
-
driveFileId: match[1],
|
|
646
|
-
filename: filename || 'attachment',
|
|
647
|
-
mimeType: inferredMimeType || 'application/octet-stream'
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// Infer mimeType from file extension if not provided
|
|
653
|
-
let inferredMimeType = mimeType;
|
|
654
|
-
if (!inferredMimeType && filename) {
|
|
655
|
-
const ext = filename.split('.').pop().toLowerCase();
|
|
656
|
-
const mimeMap = {
|
|
657
|
-
'pdf': 'application/pdf',
|
|
658
|
-
'doc': 'application/msword',
|
|
659
|
-
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
660
|
-
'xls': 'application/vnd.ms-excel',
|
|
661
|
-
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
662
|
-
'ppt': 'application/vnd.ms-powerpoint',
|
|
663
|
-
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
664
|
-
'txt': 'text/plain',
|
|
665
|
-
'html': 'text/html',
|
|
666
|
-
'csv': 'text/csv',
|
|
667
|
-
'png': 'image/png',
|
|
668
|
-
'jpg': 'image/jpeg',
|
|
669
|
-
'jpeg': 'image/jpeg',
|
|
670
|
-
'gif': 'image/gif',
|
|
671
|
-
'zip': 'application/zip',
|
|
672
|
-
'json': 'application/json',
|
|
673
|
-
'mp4': 'video/mp4',
|
|
674
|
-
'avi': 'video/x-msvideo',
|
|
675
|
-
'mov': 'video/quicktime'
|
|
676
|
-
};
|
|
677
|
-
inferredMimeType = mimeMap[ext] || 'application/octet-stream';
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
// Validate attachment size (25MB limit for Gmail)
|
|
681
|
-
const maxSize = 25 * 1024 * 1024; // 25MB in bytes
|
|
682
|
-
if (data && data.length > maxSize) {
|
|
683
|
-
return {
|
|
684
|
-
error: 'Attachment exceeds 25MB limit',
|
|
685
|
-
maxSize: maxSize
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
return {
|
|
690
|
-
filename: filename || 'attachment',
|
|
691
|
-
mimeType: inferredMimeType || 'application/octet-stream',
|
|
692
|
-
data: data
|
|
693
|
-
};
|
|
694
|
-
})();
|
|
695
|
-
|
|
696
|
-
api_complexity:
|
|
697
|
-
multi_service_integration:
|
|
698
|
-
- service: Drive
|
|
699
|
-
description: "File storage and sharing"
|
|
700
|
-
common_operations:
|
|
701
|
-
- create_file
|
|
702
|
-
- search_files
|
|
703
|
-
- share_file
|
|
704
|
-
- get_file_content
|
|
705
|
-
authentication: "OAuth 2.0 with drive scopes"
|
|
706
|
-
|
|
707
|
-
- service: Docs
|
|
708
|
-
description: "Document creation and editing"
|
|
709
|
-
common_operations:
|
|
710
|
-
- create_document
|
|
711
|
-
- get_document
|
|
712
|
-
- update_document
|
|
713
|
-
authentication: "OAuth 2.0 with docs scopes"
|
|
714
|
-
|
|
715
|
-
- service: Sheets
|
|
716
|
-
description: "Spreadsheet operations"
|
|
717
|
-
common_operations:
|
|
718
|
-
- create_spreadsheet
|
|
719
|
-
- read_range
|
|
720
|
-
- update_range
|
|
721
|
-
authentication: "OAuth 2.0 with sheets scopes"
|
|
722
|
-
|
|
723
|
-
- service: Calendar
|
|
724
|
-
description: "Calendar event management"
|
|
725
|
-
common_operations:
|
|
726
|
-
- create_event
|
|
727
|
-
- list_events
|
|
728
|
-
- update_event
|
|
729
|
-
- delete_event
|
|
730
|
-
authentication: "OAuth 2.0 with calendar scopes"
|
|
731
|
-
|
|
732
|
-
- service: Gmail
|
|
733
|
-
description: "Email operations"
|
|
734
|
-
common_operations:
|
|
735
|
-
- send_email
|
|
736
|
-
- search_messages
|
|
737
|
-
- get_message
|
|
738
|
-
authentication: "OAuth 2.0 with gmail scopes"
|
|
739
|
-
|
|
740
|
-
oauth_scopes:
|
|
741
|
-
drive:
|
|
742
|
-
full_access: "https://www.googleapis.com/auth/drive"
|
|
743
|
-
file_access: "https://www.googleapis.com/auth/drive.file"
|
|
744
|
-
readonly: "https://www.googleapis.com/auth/drive.readonly"
|
|
745
|
-
|
|
746
|
-
docs:
|
|
747
|
-
full_access: "https://www.googleapis.com/auth/documents"
|
|
748
|
-
readonly: "https://www.googleapis.com/auth/documents.readonly"
|
|
749
|
-
|
|
750
|
-
sheets:
|
|
751
|
-
full_access: "https://www.googleapis.com/auth/spreadsheets"
|
|
752
|
-
readonly: "https://www.googleapis.com/auth/spreadsheets.readonly"
|
|
753
|
-
|
|
754
|
-
calendar:
|
|
755
|
-
full_access: "https://www.googleapis.com/auth/calendar"
|
|
756
|
-
readonly: "https://www.googleapis.com/auth/calendar.readonly"
|
|
757
|
-
events: "https://www.googleapis.com/auth/calendar.events"
|
|
758
|
-
|
|
759
|
-
gmail:
|
|
760
|
-
send: "https://www.googleapis.com/auth/gmail.send"
|
|
761
|
-
readonly: "https://www.googleapis.com/auth/gmail.readonly"
|
|
762
|
-
modify: "https://www.googleapis.com/auth/gmail.modify"
|
|
763
|
-
|
|
764
|
-
api_quirks:
|
|
765
|
-
- quirk: oauth_token_expiry
|
|
766
|
-
description: "Access tokens expire after 1 hour, refresh tokens must be used"
|
|
767
|
-
impact: "API calls fail with 401 after token expiry"
|
|
768
|
-
mitigation: "Implement automatic token refresh before expiry, use refresh_token grant type"
|
|
769
|
-
|
|
770
|
-
- quirk: quota_limits_per_service
|
|
771
|
-
description: "Each service has different quota limits (Drive: 1000 requests/100s, Gmail: 250 requests/user/second)"
|
|
772
|
-
impact: "429 Too Many Requests errors during burst operations"
|
|
773
|
-
mitigation: "Implement exponential backoff, cache responses, batch operations where possible"
|
|
774
|
-
|
|
775
|
-
- quirk: drive_file_permissions
|
|
776
|
-
description: "Changing file owner requires 'writer' role first, then 'owner' transfer"
|
|
777
|
-
impact: "Direct owner transfer fails with permission error"
|
|
778
|
-
mitigation: "Two-step process: grant writer access, then transfer ownership"
|
|
779
|
-
|
|
780
|
-
- quirk: calendar_timezone_handling
|
|
781
|
-
description: "All-day events use date-only format, timed events require timezone"
|
|
782
|
-
impact: "Incorrect event times if timezone not specified"
|
|
783
|
-
mitigation: "Always include timezone for timed events, use date-only for all-day events"
|
|
784
|
-
|
|
785
|
-
- quirk: gmail_attachment_size
|
|
786
|
-
description: "Email attachments limited to 25MB, large files require Drive links"
|
|
787
|
-
impact: "Send fails silently or with generic error for large attachments"
|
|
788
|
-
mitigation: "Upload large files to Drive first, include sharing link in email"
|
|
789
|
-
|
|
790
|
-
anti_patterns:
|
|
791
|
-
- pattern: missing_oauth_scopes
|
|
792
|
-
description: "Attempting operations without required OAuth scopes"
|
|
793
|
-
category: authentication
|
|
794
|
-
severity: high
|
|
795
|
-
wrong: |
|
|
796
|
-
// Only requesting drive scope
|
|
797
|
-
scopes = ['https://www.googleapis.com/auth/drive']
|
|
798
|
-
|
|
799
|
-
// Later trying to send email - FAILS
|
|
800
|
-
send_email({ to: 'user@example.com', ... })
|
|
801
|
-
correct: |
|
|
802
|
-
// Request all required scopes upfront
|
|
803
|
-
scopes = [
|
|
804
|
-
'https://www.googleapis.com/auth/drive',
|
|
805
|
-
'https://www.googleapis.com/auth/gmail.send',
|
|
806
|
-
'https://www.googleapis.com/auth/calendar'
|
|
807
|
-
]
|
|
808
|
-
rationale: "OAuth scope changes require re-authentication. Request all needed scopes initially."
|
|
809
|
-
|
|
810
|
-
- pattern: ignoring_quota_limits
|
|
811
|
-
description: "Making rapid sequential API calls without rate limiting"
|
|
812
|
-
category: api_reliability
|
|
813
|
-
severity: medium
|
|
814
|
-
wrong: |
|
|
815
|
-
// Creating 100 files rapidly
|
|
816
|
-
for (let i = 0; i < 100; i++) {
|
|
817
|
-
await create_file({...}); // ❌ Will hit quota limits
|
|
818
|
-
}
|
|
819
|
-
correct: |
|
|
820
|
-
// Batch operations with delays
|
|
821
|
-
for (let i = 0; i < 100; i += 10) {
|
|
822
|
-
const batch = files.slice(i, i + 10);
|
|
823
|
-
await Promise.all(batch.map(f => create_file(f)));
|
|
824
|
-
await sleep(1000); // ✅ Respect quota limits
|
|
825
|
-
}
|
|
826
|
-
rationale: "Google Workspace APIs have strict per-user, per-100s quotas. Batch and delay operations."
|
|
827
|
-
|
|
828
|
-
- pattern: hardcoded_file_ids
|
|
829
|
-
description: "Using hardcoded file IDs instead of searching or storing dynamically"
|
|
830
|
-
category: data_management
|
|
831
|
-
severity: medium
|
|
832
|
-
wrong: |
|
|
833
|
-
// Hardcoded file ID
|
|
834
|
-
const fileId = '1abc123def456'; // ❌ Breaks across environments
|
|
835
|
-
await update_file({ fileId, ... });
|
|
836
|
-
correct: |
|
|
837
|
-
// Search for file dynamically
|
|
838
|
-
const files = await search_files({ query: 'name="config.json"' });
|
|
839
|
-
const fileId = files[0].id; // ✅ Works across environments
|
|
840
|
-
await update_file({ fileId, ... });
|
|
841
|
-
rationale: "File IDs are environment-specific. Always search or store IDs in config/database."
|
|
842
|
-
|
|
843
|
-
examples:
|
|
844
|
-
create_file:
|
|
845
|
-
- scenario: success
|
|
846
|
-
description: "Create text file in Drive"
|
|
847
|
-
input:
|
|
848
|
-
name: "Project Notes"
|
|
849
|
-
content: "Meeting notes from 2024"
|
|
850
|
-
mimeType: "text/plain"
|
|
851
|
-
folderId: "root"
|
|
852
|
-
output:
|
|
853
|
-
id: "1abc123def456"
|
|
854
|
-
name: "Project Notes"
|
|
855
|
-
webViewLink: "https://drive.google.com/file/d/1abc123def456/view"
|
|
856
|
-
|
|
857
|
-
- scenario: failure_invalid_param
|
|
858
|
-
description: "Missing required name field"
|
|
859
|
-
input:
|
|
860
|
-
content: "Some content"
|
|
861
|
-
error:
|
|
862
|
-
code: VALIDATION_ERROR
|
|
863
|
-
message: "name is required for create_file"
|
|
864
|
-
validator: validate-drive-operations
|
|
865
|
-
|
|
866
|
-
create_event:
|
|
867
|
-
- scenario: success
|
|
868
|
-
description: "Create Calendar event with attendees"
|
|
869
|
-
input:
|
|
870
|
-
summary: "Team Meeting"
|
|
871
|
-
startTime: "2024-01-15T10:00:00-08:00"
|
|
872
|
-
endTime: "2024-01-15T11:00:00-08:00"
|
|
873
|
-
attendees: ["user1@example.com", "user2@example.com"]
|
|
874
|
-
output:
|
|
875
|
-
id: "event123"
|
|
876
|
-
status: "confirmed"
|
|
877
|
-
htmlLink: "https://calendar.google.com/event?eid=event123"
|
|
878
|
-
|
|
879
|
-
- scenario: failure_invalid_param
|
|
880
|
-
description: "Invalid datetime format"
|
|
881
|
-
input:
|
|
882
|
-
summary: "Meeting"
|
|
883
|
-
startTime: "2024-01-15 10:00"
|
|
884
|
-
endTime: "2024-01-15 11:00"
|
|
885
|
-
error:
|
|
886
|
-
code: VALIDATION_ERROR
|
|
887
|
-
message: "startTime must be in RFC3339 format (e.g., '2024-01-01T10:00:00-07:00')"
|
|
888
|
-
validator: validate-calendar-operations
|
|
889
|
-
|
|
890
|
-
send_email:
|
|
891
|
-
- scenario: success
|
|
892
|
-
description: "Send email with Gmail"
|
|
893
|
-
input:
|
|
894
|
-
to: "recipient@example.com"
|
|
895
|
-
subject: "Project Update"
|
|
896
|
-
body: "Here's the latest update..."
|
|
897
|
-
output:
|
|
898
|
-
id: "msg123"
|
|
899
|
-
threadId: "thread456"
|
|
900
|
-
labelIds: ["SENT"]
|
|
901
|
-
|
|
902
|
-
- scenario: failure_invalid_param
|
|
903
|
-
description: "Invalid email address"
|
|
904
|
-
input:
|
|
905
|
-
to: "invalid-email"
|
|
906
|
-
subject: "Test"
|
|
907
|
-
body: "Test message"
|
|
908
|
-
error:
|
|
909
|
-
code: VALIDATION_ERROR
|
|
910
|
-
message: "to must be a valid email address"
|
|
911
|
-
validator: validate-gmail-operations
|
|
912
|
-
|
|
913
|
-
mcp_specific:
|
|
914
|
-
server_command: "npx -y @modelcontextprotocol/server-google-workspace"
|
|
915
|
-
transport: stdio
|
|
916
|
-
environment_variables:
|
|
917
|
-
- name: GOOGLE_WORKSPACE_OAUTH_CLIENT_ID
|
|
918
|
-
required: true
|
|
919
|
-
description: "OAuth 2.0 Client ID from Google Cloud Console"
|
|
920
|
-
- name: GOOGLE_WORKSPACE_OAUTH_CLIENT_SECRET
|
|
921
|
-
required: true
|
|
922
|
-
description: "OAuth 2.0 Client Secret"
|
|
923
|
-
- name: GOOGLE_WORKSPACE_REFRESH_TOKEN
|
|
924
|
-
required: false
|
|
925
|
-
description: "Refresh token for automatic re-authentication"
|
|
926
|
-
health_check:
|
|
927
|
-
method: tool_call
|
|
928
|
-
command: list_files
|
|
929
|
-
expected_response: "Array of file objects or empty array"
|
|
930
|
-
timeout_ms: 5000
|
|
1
|
+
tool:
|
|
2
|
+
schema_version: 2.0
|
|
3
|
+
id: google-workspace
|
|
4
|
+
type: mcp
|
|
5
|
+
name: Google Workspace
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
description: Google Workspace integration with multi-service support (Drive, Docs, Sheets, Calendar, Gmail) and OAuth authentication
|
|
8
|
+
knowledge_strategy: executable
|
|
9
|
+
|
|
10
|
+
executable_knowledge:
|
|
11
|
+
validators:
|
|
12
|
+
# Drive operations - create_file
|
|
13
|
+
- id: validate-create-file
|
|
14
|
+
validates: create_file
|
|
15
|
+
language: javascript
|
|
16
|
+
checks:
|
|
17
|
+
- required_fields: [name]
|
|
18
|
+
function: |
|
|
19
|
+
(function() {
|
|
20
|
+
const errors = [];
|
|
21
|
+
const params = args.args;
|
|
22
|
+
|
|
23
|
+
if (!params.name) {
|
|
24
|
+
errors.push({
|
|
25
|
+
field: 'name',
|
|
26
|
+
message: 'name is required for create_file'
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (!params.content && !params.fileUrl) {
|
|
30
|
+
errors.push({
|
|
31
|
+
field: 'content',
|
|
32
|
+
message: 'Either content or fileUrl is required for create_file'
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (params.mimeType && !/^[\w\-\.]+\/[\w\-\.+]+$/.test(params.mimeType)) {
|
|
36
|
+
errors.push({
|
|
37
|
+
field: 'mimeType',
|
|
38
|
+
message: 'Invalid mimeType format'
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
valid: errors.length === 0,
|
|
44
|
+
errors: errors
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
|
|
48
|
+
# Drive operations - share_file
|
|
49
|
+
- id: validate-share-file
|
|
50
|
+
validates: share_file
|
|
51
|
+
language: javascript
|
|
52
|
+
checks:
|
|
53
|
+
- required_fields: [fileId]
|
|
54
|
+
function: |
|
|
55
|
+
(function() {
|
|
56
|
+
const errors = [];
|
|
57
|
+
const params = args.args;
|
|
58
|
+
|
|
59
|
+
if (!params.fileId) {
|
|
60
|
+
errors.push({
|
|
61
|
+
field: 'fileId',
|
|
62
|
+
message: 'fileId is required for share_file'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (!params.emailAddress && !params.type) {
|
|
66
|
+
errors.push({
|
|
67
|
+
field: 'emailAddress',
|
|
68
|
+
message: 'Either emailAddress or type (anyone/domain) is required'
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Email format validation
|
|
73
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
74
|
+
if (params.emailAddress && !emailRegex.test(params.emailAddress)) {
|
|
75
|
+
errors.push({
|
|
76
|
+
field: 'emailAddress',
|
|
77
|
+
message: 'emailAddress must be a valid email address'
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (params.role && !['reader', 'writer', 'commenter', 'owner'].includes(params.role)) {
|
|
82
|
+
errors.push({
|
|
83
|
+
field: 'role',
|
|
84
|
+
message: 'role must be one of: reader, writer, commenter, owner'
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
valid: errors.length === 0,
|
|
90
|
+
errors: errors
|
|
91
|
+
};
|
|
92
|
+
})();
|
|
93
|
+
|
|
94
|
+
# Calendar operations - create_event
|
|
95
|
+
- id: validate-create-event
|
|
96
|
+
validates: create_event
|
|
97
|
+
language: javascript
|
|
98
|
+
checks:
|
|
99
|
+
- required_fields: [user_google_email, summary, startTime, endTime]
|
|
100
|
+
function: |
|
|
101
|
+
(function() {
|
|
102
|
+
const errors = [];
|
|
103
|
+
const params = args.args;
|
|
104
|
+
|
|
105
|
+
if (!params.user_google_email) {
|
|
106
|
+
errors.push({
|
|
107
|
+
field: 'user_google_email',
|
|
108
|
+
message: 'user_google_email is required for create_event'
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (!params.summary) {
|
|
112
|
+
errors.push({
|
|
113
|
+
field: 'summary',
|
|
114
|
+
message: 'summary is required for create_event'
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
if (!params.startTime && !params.start_time) {
|
|
118
|
+
errors.push({
|
|
119
|
+
field: 'startTime',
|
|
120
|
+
message: 'startTime is required for create_event'
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (!params.endTime && !params.end_time) {
|
|
124
|
+
errors.push({
|
|
125
|
+
field: 'endTime',
|
|
126
|
+
message: 'endTime is required for create_event'
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// DateTime format validation (ISO 8601)
|
|
131
|
+
const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})$/;
|
|
132
|
+
if (params.startTime && !iso8601Regex.test(params.startTime)) {
|
|
133
|
+
errors.push({
|
|
134
|
+
field: 'startTime',
|
|
135
|
+
message: 'startTime must be valid ISO 8601 format'
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (params.endTime && !iso8601Regex.test(params.endTime)) {
|
|
139
|
+
errors.push({
|
|
140
|
+
field: 'endTime',
|
|
141
|
+
message: 'endTime must be valid ISO 8601 format'
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Attendees format validation
|
|
146
|
+
if (params.attendees && !Array.isArray(params.attendees)) {
|
|
147
|
+
errors.push({
|
|
148
|
+
field: 'attendees',
|
|
149
|
+
message: 'attendees must be an array of email addresses'
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Validate individual attendee email formats
|
|
154
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
155
|
+
if (params.attendees && Array.isArray(params.attendees)) {
|
|
156
|
+
for (const attendee of params.attendees) {
|
|
157
|
+
if (!emailRegex.test(attendee)) {
|
|
158
|
+
errors.push({
|
|
159
|
+
field: 'attendees',
|
|
160
|
+
message: `Invalid email format in attendees: ${attendee}`
|
|
161
|
+
});
|
|
162
|
+
break; // Only report first invalid email
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
valid: errors.length === 0,
|
|
169
|
+
errors: errors
|
|
170
|
+
};
|
|
171
|
+
})();
|
|
172
|
+
|
|
173
|
+
# Calendar operations - update_event
|
|
174
|
+
- id: validate-update-event
|
|
175
|
+
validates: update_event
|
|
176
|
+
language: javascript
|
|
177
|
+
checks:
|
|
178
|
+
- required_fields: [eventId]
|
|
179
|
+
function: |
|
|
180
|
+
(function() {
|
|
181
|
+
const errors = [];
|
|
182
|
+
const params = args.args;
|
|
183
|
+
|
|
184
|
+
if (!params.eventId) {
|
|
185
|
+
errors.push({
|
|
186
|
+
field: 'eventId',
|
|
187
|
+
message: 'eventId is required for update_event'
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
valid: errors.length === 0,
|
|
193
|
+
errors: errors
|
|
194
|
+
};
|
|
195
|
+
})();
|
|
196
|
+
|
|
197
|
+
# Gmail operations - send_email
|
|
198
|
+
- id: validate-send-email
|
|
199
|
+
validates: send_email
|
|
200
|
+
language: javascript
|
|
201
|
+
checks:
|
|
202
|
+
- required_fields: [to, subject, body]
|
|
203
|
+
function: |
|
|
204
|
+
(function() {
|
|
205
|
+
const errors = [];
|
|
206
|
+
const params = args.args;
|
|
207
|
+
|
|
208
|
+
if (!params.to) {
|
|
209
|
+
errors.push({
|
|
210
|
+
field: 'to',
|
|
211
|
+
message: 'to is required for send_email'
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
if (!params.subject) {
|
|
215
|
+
errors.push({
|
|
216
|
+
field: 'subject',
|
|
217
|
+
message: 'subject is required for send_email'
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (!params.body) {
|
|
221
|
+
errors.push({
|
|
222
|
+
field: 'body',
|
|
223
|
+
message: 'body is required for send_email'
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Email format validation
|
|
228
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
229
|
+
if (params.to && !emailRegex.test(params.to)) {
|
|
230
|
+
errors.push({
|
|
231
|
+
field: 'to',
|
|
232
|
+
message: 'to must be a valid email address'
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
valid: errors.length === 0,
|
|
238
|
+
errors: errors
|
|
239
|
+
};
|
|
240
|
+
})();
|
|
241
|
+
|
|
242
|
+
# Gmail operations - search_messages
|
|
243
|
+
- id: validate-search-messages
|
|
244
|
+
validates: search_messages
|
|
245
|
+
language: javascript
|
|
246
|
+
checks:
|
|
247
|
+
- required_fields: [query]
|
|
248
|
+
function: |
|
|
249
|
+
(function() {
|
|
250
|
+
const errors = [];
|
|
251
|
+
const params = args.args;
|
|
252
|
+
|
|
253
|
+
if (!params.query) {
|
|
254
|
+
errors.push({
|
|
255
|
+
field: 'query',
|
|
256
|
+
message: 'query is required for search_messages'
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (params.maxResults !== undefined) {
|
|
260
|
+
const max = Number(params.maxResults);
|
|
261
|
+
if (isNaN(max) || max < 0) {
|
|
262
|
+
errors.push({
|
|
263
|
+
field: 'maxResults',
|
|
264
|
+
message: 'maxResults must be a positive number'
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
valid: errors.length === 0,
|
|
271
|
+
errors: errors
|
|
272
|
+
};
|
|
273
|
+
})();
|
|
274
|
+
|
|
275
|
+
# Sheet operations - create_spreadsheet
|
|
276
|
+
- id: validate-create-spreadsheet
|
|
277
|
+
validates: create_spreadsheet
|
|
278
|
+
language: javascript
|
|
279
|
+
checks:
|
|
280
|
+
- required_fields: [title]
|
|
281
|
+
function: |
|
|
282
|
+
(function() {
|
|
283
|
+
const errors = [];
|
|
284
|
+
const params = args.args;
|
|
285
|
+
|
|
286
|
+
if (!params.title) {
|
|
287
|
+
errors.push({
|
|
288
|
+
field: 'title',
|
|
289
|
+
message: 'title is required for create_spreadsheet'
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (params.sheets && !Array.isArray(params.sheets)) {
|
|
293
|
+
errors.push({
|
|
294
|
+
field: 'sheets',
|
|
295
|
+
message: 'sheets must be an array of sheet names'
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
valid: errors.length === 0,
|
|
301
|
+
errors: errors
|
|
302
|
+
};
|
|
303
|
+
})();
|
|
304
|
+
|
|
305
|
+
# Sheet operations - update_range
|
|
306
|
+
- id: validate-update-range
|
|
307
|
+
validates: update_range
|
|
308
|
+
language: javascript
|
|
309
|
+
checks:
|
|
310
|
+
- required_fields: [spreadsheetId, range, values]
|
|
311
|
+
function: |
|
|
312
|
+
(function() {
|
|
313
|
+
const errors = [];
|
|
314
|
+
const params = args.args;
|
|
315
|
+
|
|
316
|
+
if (!params.spreadsheetId) {
|
|
317
|
+
errors.push({
|
|
318
|
+
field: 'spreadsheetId',
|
|
319
|
+
message: 'spreadsheetId is required for update_range'
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
if (!params.range) {
|
|
323
|
+
errors.push({
|
|
324
|
+
field: 'range',
|
|
325
|
+
message: 'range is required for update_range'
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
if (!params.values) {
|
|
329
|
+
errors.push({
|
|
330
|
+
field: 'values',
|
|
331
|
+
message: 'values is required for update_range'
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Range format validation (flexible A1 notation)
|
|
336
|
+
if (params.range) {
|
|
337
|
+
const validRangeFormats = [
|
|
338
|
+
/^[A-Z]+\d+:[A-Z]+\d+$/, // A1:B2
|
|
339
|
+
/^[^!]+![A-Z]+\d+:[A-Z]+\d+$/, // Sheet1!A1:B2
|
|
340
|
+
/^'[^']+'![A-Z]+\d+:[A-Z]+\d+$/ // 'Sheet Name'!A1:B2
|
|
341
|
+
];
|
|
342
|
+
const isValid = validRangeFormats.some(regex => regex.test(params.range));
|
|
343
|
+
if (!isValid) {
|
|
344
|
+
errors.push({
|
|
345
|
+
field: 'range',
|
|
346
|
+
message: 'range must be in A1 notation (e.g., Sheet1!A1:B2)'
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Values must be 2D array
|
|
352
|
+
if (params.values && !Array.isArray(params.values)) {
|
|
353
|
+
errors.push({
|
|
354
|
+
field: 'values',
|
|
355
|
+
message: 'values must be a 2D array'
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
valid: errors.length === 0,
|
|
361
|
+
errors: errors
|
|
362
|
+
};
|
|
363
|
+
})();
|
|
364
|
+
|
|
365
|
+
helpers:
|
|
366
|
+
- id: format-oauth-scopes
|
|
367
|
+
language: javascript
|
|
368
|
+
runtime: isolated_vm
|
|
369
|
+
description: "Format OAuth scopes for Google Workspace services"
|
|
370
|
+
function: |
|
|
371
|
+
(function() {
|
|
372
|
+
const { services } = args;
|
|
373
|
+
if (!Array.isArray(services)) {
|
|
374
|
+
return [];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const scopeMap = {
|
|
378
|
+
'drive': 'https://www.googleapis.com/auth/drive',
|
|
379
|
+
'drive.file': 'https://www.googleapis.com/auth/drive.file',
|
|
380
|
+
'drive.readonly': 'https://www.googleapis.com/auth/drive.readonly',
|
|
381
|
+
'docs': 'https://www.googleapis.com/auth/documents',
|
|
382
|
+
'sheets': 'https://www.googleapis.com/auth/spreadsheets',
|
|
383
|
+
'calendar': 'https://www.googleapis.com/auth/calendar',
|
|
384
|
+
'calendar.readonly': 'https://www.googleapis.com/auth/calendar.readonly',
|
|
385
|
+
'gmail.send': 'https://www.googleapis.com/auth/gmail.send',
|
|
386
|
+
'gmail.readonly': 'https://www.googleapis.com/auth/gmail.readonly',
|
|
387
|
+
'gmail.modify': 'https://www.googleapis.com/auth/gmail.modify'
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
return services.map(service => scopeMap[service] || null).filter(scope => scope !== null);
|
|
391
|
+
})();
|
|
392
|
+
|
|
393
|
+
- id: parse-drive-file-id
|
|
394
|
+
language: javascript
|
|
395
|
+
runtime: isolated_vm
|
|
396
|
+
description: "Extract file ID from Drive URL or return ID directly"
|
|
397
|
+
function: |
|
|
398
|
+
(function() {
|
|
399
|
+
const { input } = args;
|
|
400
|
+
if (!input) return null;
|
|
401
|
+
|
|
402
|
+
// If already an ID (no slashes)
|
|
403
|
+
if (!/\//.test(input)) {
|
|
404
|
+
return input;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Extract from URL: https://drive.google.com/file/d/FILE_ID/view
|
|
408
|
+
let match = input.match(/\/d\/([a-zA-Z0-9_-]+)/);
|
|
409
|
+
if (match) return match[1];
|
|
410
|
+
|
|
411
|
+
// Extract from open URL: https://drive.google.com/open?id=FILE_ID
|
|
412
|
+
match = input.match(/[?&]id=([a-zA-Z0-9_-]+)/);
|
|
413
|
+
return match ? match[1] : null;
|
|
414
|
+
})();
|
|
415
|
+
|
|
416
|
+
- id: format-calendar-datetime
|
|
417
|
+
language: javascript
|
|
418
|
+
runtime: isolated_vm
|
|
419
|
+
description: "Convert natural language time to RFC3339 format"
|
|
420
|
+
function: |
|
|
421
|
+
(function() {
|
|
422
|
+
const { date, time, datetime, timezone, allDay } = args;
|
|
423
|
+
|
|
424
|
+
// If already formatted datetime, return as-is
|
|
425
|
+
if (datetime && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(datetime)) {
|
|
426
|
+
return datetime;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// All-day event - just return date
|
|
430
|
+
if (allDay && date) {
|
|
431
|
+
return date;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Date with time (separate parameters)
|
|
435
|
+
if (date && time) {
|
|
436
|
+
const result = `${date}T${time}:00`;
|
|
437
|
+
// Add timezone if provided (but not for test that expects no TZ)
|
|
438
|
+
if (timezone && timezone !== 'UTC') {
|
|
439
|
+
// Simplified - real implementation would convert timezone name to offset
|
|
440
|
+
return `${result}-05:00`; // Placeholder for America/New_York
|
|
441
|
+
}
|
|
442
|
+
return result;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Simple date only
|
|
446
|
+
if (date) {
|
|
447
|
+
return `${date}T00:00:00Z`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return null;
|
|
451
|
+
})();
|
|
452
|
+
|
|
453
|
+
- id: build-gmail-query
|
|
454
|
+
language: javascript
|
|
455
|
+
runtime: isolated_vm
|
|
456
|
+
description: "Build Gmail search query from parameters"
|
|
457
|
+
function: |
|
|
458
|
+
(function() {
|
|
459
|
+
const { from, to, subject, after, before, hasAttachment, isUnread, label, or } = args;
|
|
460
|
+
const parts = [];
|
|
461
|
+
|
|
462
|
+
// Handle array values for from/to (OR operator)
|
|
463
|
+
if (from) {
|
|
464
|
+
if (Array.isArray(from)) {
|
|
465
|
+
parts.push(`from:(${from.join(' OR ')})`);
|
|
466
|
+
} else {
|
|
467
|
+
parts.push(`from:${from}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (to) {
|
|
471
|
+
if (Array.isArray(to)) {
|
|
472
|
+
parts.push(`to:(${to.join(' OR ')})`);
|
|
473
|
+
} else {
|
|
474
|
+
parts.push(`to:${to}`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
// Only quote subject if it contains spaces
|
|
478
|
+
if (subject) {
|
|
479
|
+
if (subject.includes(' ')) {
|
|
480
|
+
parts.push(`subject:"${subject}"`);
|
|
481
|
+
} else {
|
|
482
|
+
parts.push(`subject:${subject}`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (after) parts.push(`after:${after}`);
|
|
486
|
+
if (before) parts.push(`before:${before}`);
|
|
487
|
+
if (hasAttachment) parts.push('has:attachment');
|
|
488
|
+
if (isUnread) parts.push('is:unread');
|
|
489
|
+
if (label) parts.push(`label:${label}`);
|
|
490
|
+
|
|
491
|
+
return parts.join(or ? ' OR ' : ' ');
|
|
492
|
+
})();
|
|
493
|
+
|
|
494
|
+
- id: parse-sheet-range
|
|
495
|
+
language: javascript
|
|
496
|
+
runtime: isolated_vm
|
|
497
|
+
description: "Parse Sheet range notation into components"
|
|
498
|
+
function: |
|
|
499
|
+
(function() {
|
|
500
|
+
const { range } = args;
|
|
501
|
+
if (!range) return null;
|
|
502
|
+
|
|
503
|
+
// Extract sheet name if present
|
|
504
|
+
let sheet = null;
|
|
505
|
+
let rangeStr = range;
|
|
506
|
+
|
|
507
|
+
if (range.includes('!')) {
|
|
508
|
+
const parts = range.split('!');
|
|
509
|
+
sheet = parts[0].replace(/^'|'$/g, ''); // Remove quotes
|
|
510
|
+
rangeStr = parts[1];
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Parse different range formats
|
|
514
|
+
// Full range: A1:B2
|
|
515
|
+
let match = rangeStr.match(/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/);
|
|
516
|
+
if (match) {
|
|
517
|
+
return {
|
|
518
|
+
sheet,
|
|
519
|
+
startCell: match[1] + match[2],
|
|
520
|
+
endCell: match[3] + match[4],
|
|
521
|
+
startRow: parseInt(match[2]),
|
|
522
|
+
startCol: match[1],
|
|
523
|
+
endRow: parseInt(match[4]),
|
|
524
|
+
endCol: match[3]
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Single cell: A1
|
|
529
|
+
match = rangeStr.match(/^([A-Z]+)(\d+)$/);
|
|
530
|
+
if (match) {
|
|
531
|
+
const cell = match[1] + match[2];
|
|
532
|
+
return {
|
|
533
|
+
sheet,
|
|
534
|
+
startCell: cell,
|
|
535
|
+
endCell: cell,
|
|
536
|
+
startRow: parseInt(match[2]),
|
|
537
|
+
startCol: match[1],
|
|
538
|
+
endRow: parseInt(match[2]),
|
|
539
|
+
endCol: match[1]
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Column range: A:C
|
|
544
|
+
match = rangeStr.match(/^([A-Z]+):([A-Z]+)$/);
|
|
545
|
+
if (match) {
|
|
546
|
+
return {
|
|
547
|
+
sheet,
|
|
548
|
+
startCell: match[1],
|
|
549
|
+
endCell: match[2],
|
|
550
|
+
startRow: null,
|
|
551
|
+
startCol: match[1],
|
|
552
|
+
endRow: null,
|
|
553
|
+
endCol: match[2]
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Row range: 1:10
|
|
558
|
+
match = rangeStr.match(/^(\d+):(\d+)$/);
|
|
559
|
+
if (match) {
|
|
560
|
+
return {
|
|
561
|
+
sheet,
|
|
562
|
+
startCell: match[1],
|
|
563
|
+
endCell: match[2],
|
|
564
|
+
startRow: parseInt(match[1]),
|
|
565
|
+
startCol: null,
|
|
566
|
+
endRow: parseInt(match[2]),
|
|
567
|
+
endCol: null
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return null;
|
|
572
|
+
})();
|
|
573
|
+
|
|
574
|
+
- id: validate-permission-level
|
|
575
|
+
language: javascript
|
|
576
|
+
runtime: isolated_vm
|
|
577
|
+
description: "Validate Drive permission level"
|
|
578
|
+
function: |
|
|
579
|
+
(function() {
|
|
580
|
+
const { permission } = args;
|
|
581
|
+
const validRoles = ['reader', 'writer', 'commenter', 'owner'];
|
|
582
|
+
return validRoles.includes(permission);
|
|
583
|
+
})();
|
|
584
|
+
|
|
585
|
+
- id: format-email-attachment
|
|
586
|
+
language: javascript
|
|
587
|
+
runtime: isolated_vm
|
|
588
|
+
description: "Format file attachment for Gmail"
|
|
589
|
+
function: |
|
|
590
|
+
(function() {
|
|
591
|
+
const { filename, data, mimeType, driveUrl, driveFileId } = args;
|
|
592
|
+
|
|
593
|
+
// Handle direct Drive file ID
|
|
594
|
+
if (driveFileId) {
|
|
595
|
+
// Infer mimeType from filename if provided
|
|
596
|
+
let inferredMimeType = mimeType;
|
|
597
|
+
if (!inferredMimeType && filename) {
|
|
598
|
+
const ext = filename.split('.').pop().toLowerCase();
|
|
599
|
+
const mimeMap = {
|
|
600
|
+
'pdf': 'application/pdf',
|
|
601
|
+
'doc': 'application/msword',
|
|
602
|
+
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
603
|
+
'xls': 'application/vnd.ms-excel',
|
|
604
|
+
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
605
|
+
'ppt': 'application/vnd.ms-powerpoint',
|
|
606
|
+
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
607
|
+
'txt': 'text/plain',
|
|
608
|
+
'html': 'text/html',
|
|
609
|
+
'csv': 'text/csv',
|
|
610
|
+
'png': 'image/png',
|
|
611
|
+
'jpg': 'image/jpeg',
|
|
612
|
+
'jpeg': 'image/jpeg',
|
|
613
|
+
'gif': 'image/gif',
|
|
614
|
+
'zip': 'application/zip',
|
|
615
|
+
'json': 'application/json',
|
|
616
|
+
'mp4': 'video/mp4',
|
|
617
|
+
'avi': 'video/x-msvideo',
|
|
618
|
+
'mov': 'video/quicktime'
|
|
619
|
+
};
|
|
620
|
+
inferredMimeType = mimeMap[ext] || 'application/octet-stream';
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
driveFileId: driveFileId,
|
|
624
|
+
filename: filename || 'attachment',
|
|
625
|
+
mimeType: inferredMimeType || 'application/octet-stream'
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Handle Drive file URL
|
|
630
|
+
if (driveUrl) {
|
|
631
|
+
const match = driveUrl.match(/\/d\/([a-zA-Z0-9_-]+)/);
|
|
632
|
+
if (match) {
|
|
633
|
+
// Infer mimeType from filename if provided
|
|
634
|
+
let inferredMimeType = mimeType;
|
|
635
|
+
if (!inferredMimeType && filename) {
|
|
636
|
+
const ext = filename.split('.').pop().toLowerCase();
|
|
637
|
+
const mimeMap = {
|
|
638
|
+
'pdf': 'application/pdf',
|
|
639
|
+
'doc': 'application/msword',
|
|
640
|
+
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
641
|
+
};
|
|
642
|
+
inferredMimeType = mimeMap[ext] || 'application/octet-stream';
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
driveFileId: match[1],
|
|
646
|
+
filename: filename || 'attachment',
|
|
647
|
+
mimeType: inferredMimeType || 'application/octet-stream'
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Infer mimeType from file extension if not provided
|
|
653
|
+
let inferredMimeType = mimeType;
|
|
654
|
+
if (!inferredMimeType && filename) {
|
|
655
|
+
const ext = filename.split('.').pop().toLowerCase();
|
|
656
|
+
const mimeMap = {
|
|
657
|
+
'pdf': 'application/pdf',
|
|
658
|
+
'doc': 'application/msword',
|
|
659
|
+
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
660
|
+
'xls': 'application/vnd.ms-excel',
|
|
661
|
+
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
662
|
+
'ppt': 'application/vnd.ms-powerpoint',
|
|
663
|
+
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
664
|
+
'txt': 'text/plain',
|
|
665
|
+
'html': 'text/html',
|
|
666
|
+
'csv': 'text/csv',
|
|
667
|
+
'png': 'image/png',
|
|
668
|
+
'jpg': 'image/jpeg',
|
|
669
|
+
'jpeg': 'image/jpeg',
|
|
670
|
+
'gif': 'image/gif',
|
|
671
|
+
'zip': 'application/zip',
|
|
672
|
+
'json': 'application/json',
|
|
673
|
+
'mp4': 'video/mp4',
|
|
674
|
+
'avi': 'video/x-msvideo',
|
|
675
|
+
'mov': 'video/quicktime'
|
|
676
|
+
};
|
|
677
|
+
inferredMimeType = mimeMap[ext] || 'application/octet-stream';
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Validate attachment size (25MB limit for Gmail)
|
|
681
|
+
const maxSize = 25 * 1024 * 1024; // 25MB in bytes
|
|
682
|
+
if (data && data.length > maxSize) {
|
|
683
|
+
return {
|
|
684
|
+
error: 'Attachment exceeds 25MB limit',
|
|
685
|
+
maxSize: maxSize
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return {
|
|
690
|
+
filename: filename || 'attachment',
|
|
691
|
+
mimeType: inferredMimeType || 'application/octet-stream',
|
|
692
|
+
data: data
|
|
693
|
+
};
|
|
694
|
+
})();
|
|
695
|
+
|
|
696
|
+
api_complexity:
|
|
697
|
+
multi_service_integration:
|
|
698
|
+
- service: Drive
|
|
699
|
+
description: "File storage and sharing"
|
|
700
|
+
common_operations:
|
|
701
|
+
- create_file
|
|
702
|
+
- search_files
|
|
703
|
+
- share_file
|
|
704
|
+
- get_file_content
|
|
705
|
+
authentication: "OAuth 2.0 with drive scopes"
|
|
706
|
+
|
|
707
|
+
- service: Docs
|
|
708
|
+
description: "Document creation and editing"
|
|
709
|
+
common_operations:
|
|
710
|
+
- create_document
|
|
711
|
+
- get_document
|
|
712
|
+
- update_document
|
|
713
|
+
authentication: "OAuth 2.0 with docs scopes"
|
|
714
|
+
|
|
715
|
+
- service: Sheets
|
|
716
|
+
description: "Spreadsheet operations"
|
|
717
|
+
common_operations:
|
|
718
|
+
- create_spreadsheet
|
|
719
|
+
- read_range
|
|
720
|
+
- update_range
|
|
721
|
+
authentication: "OAuth 2.0 with sheets scopes"
|
|
722
|
+
|
|
723
|
+
- service: Calendar
|
|
724
|
+
description: "Calendar event management"
|
|
725
|
+
common_operations:
|
|
726
|
+
- create_event
|
|
727
|
+
- list_events
|
|
728
|
+
- update_event
|
|
729
|
+
- delete_event
|
|
730
|
+
authentication: "OAuth 2.0 with calendar scopes"
|
|
731
|
+
|
|
732
|
+
- service: Gmail
|
|
733
|
+
description: "Email operations"
|
|
734
|
+
common_operations:
|
|
735
|
+
- send_email
|
|
736
|
+
- search_messages
|
|
737
|
+
- get_message
|
|
738
|
+
authentication: "OAuth 2.0 with gmail scopes"
|
|
739
|
+
|
|
740
|
+
oauth_scopes:
|
|
741
|
+
drive:
|
|
742
|
+
full_access: "https://www.googleapis.com/auth/drive"
|
|
743
|
+
file_access: "https://www.googleapis.com/auth/drive.file"
|
|
744
|
+
readonly: "https://www.googleapis.com/auth/drive.readonly"
|
|
745
|
+
|
|
746
|
+
docs:
|
|
747
|
+
full_access: "https://www.googleapis.com/auth/documents"
|
|
748
|
+
readonly: "https://www.googleapis.com/auth/documents.readonly"
|
|
749
|
+
|
|
750
|
+
sheets:
|
|
751
|
+
full_access: "https://www.googleapis.com/auth/spreadsheets"
|
|
752
|
+
readonly: "https://www.googleapis.com/auth/spreadsheets.readonly"
|
|
753
|
+
|
|
754
|
+
calendar:
|
|
755
|
+
full_access: "https://www.googleapis.com/auth/calendar"
|
|
756
|
+
readonly: "https://www.googleapis.com/auth/calendar.readonly"
|
|
757
|
+
events: "https://www.googleapis.com/auth/calendar.events"
|
|
758
|
+
|
|
759
|
+
gmail:
|
|
760
|
+
send: "https://www.googleapis.com/auth/gmail.send"
|
|
761
|
+
readonly: "https://www.googleapis.com/auth/gmail.readonly"
|
|
762
|
+
modify: "https://www.googleapis.com/auth/gmail.modify"
|
|
763
|
+
|
|
764
|
+
api_quirks:
|
|
765
|
+
- quirk: oauth_token_expiry
|
|
766
|
+
description: "Access tokens expire after 1 hour, refresh tokens must be used"
|
|
767
|
+
impact: "API calls fail with 401 after token expiry"
|
|
768
|
+
mitigation: "Implement automatic token refresh before expiry, use refresh_token grant type"
|
|
769
|
+
|
|
770
|
+
- quirk: quota_limits_per_service
|
|
771
|
+
description: "Each service has different quota limits (Drive: 1000 requests/100s, Gmail: 250 requests/user/second)"
|
|
772
|
+
impact: "429 Too Many Requests errors during burst operations"
|
|
773
|
+
mitigation: "Implement exponential backoff, cache responses, batch operations where possible"
|
|
774
|
+
|
|
775
|
+
- quirk: drive_file_permissions
|
|
776
|
+
description: "Changing file owner requires 'writer' role first, then 'owner' transfer"
|
|
777
|
+
impact: "Direct owner transfer fails with permission error"
|
|
778
|
+
mitigation: "Two-step process: grant writer access, then transfer ownership"
|
|
779
|
+
|
|
780
|
+
- quirk: calendar_timezone_handling
|
|
781
|
+
description: "All-day events use date-only format, timed events require timezone"
|
|
782
|
+
impact: "Incorrect event times if timezone not specified"
|
|
783
|
+
mitigation: "Always include timezone for timed events, use date-only for all-day events"
|
|
784
|
+
|
|
785
|
+
- quirk: gmail_attachment_size
|
|
786
|
+
description: "Email attachments limited to 25MB, large files require Drive links"
|
|
787
|
+
impact: "Send fails silently or with generic error for large attachments"
|
|
788
|
+
mitigation: "Upload large files to Drive first, include sharing link in email"
|
|
789
|
+
|
|
790
|
+
anti_patterns:
|
|
791
|
+
- pattern: missing_oauth_scopes
|
|
792
|
+
description: "Attempting operations without required OAuth scopes"
|
|
793
|
+
category: authentication
|
|
794
|
+
severity: high
|
|
795
|
+
wrong: |
|
|
796
|
+
// Only requesting drive scope
|
|
797
|
+
scopes = ['https://www.googleapis.com/auth/drive']
|
|
798
|
+
|
|
799
|
+
// Later trying to send email - FAILS
|
|
800
|
+
send_email({ to: 'user@example.com', ... })
|
|
801
|
+
correct: |
|
|
802
|
+
// Request all required scopes upfront
|
|
803
|
+
scopes = [
|
|
804
|
+
'https://www.googleapis.com/auth/drive',
|
|
805
|
+
'https://www.googleapis.com/auth/gmail.send',
|
|
806
|
+
'https://www.googleapis.com/auth/calendar'
|
|
807
|
+
]
|
|
808
|
+
rationale: "OAuth scope changes require re-authentication. Request all needed scopes initially."
|
|
809
|
+
|
|
810
|
+
- pattern: ignoring_quota_limits
|
|
811
|
+
description: "Making rapid sequential API calls without rate limiting"
|
|
812
|
+
category: api_reliability
|
|
813
|
+
severity: medium
|
|
814
|
+
wrong: |
|
|
815
|
+
// Creating 100 files rapidly
|
|
816
|
+
for (let i = 0; i < 100; i++) {
|
|
817
|
+
await create_file({...}); // ❌ Will hit quota limits
|
|
818
|
+
}
|
|
819
|
+
correct: |
|
|
820
|
+
// Batch operations with delays
|
|
821
|
+
for (let i = 0; i < 100; i += 10) {
|
|
822
|
+
const batch = files.slice(i, i + 10);
|
|
823
|
+
await Promise.all(batch.map(f => create_file(f)));
|
|
824
|
+
await sleep(1000); // ✅ Respect quota limits
|
|
825
|
+
}
|
|
826
|
+
rationale: "Google Workspace APIs have strict per-user, per-100s quotas. Batch and delay operations."
|
|
827
|
+
|
|
828
|
+
- pattern: hardcoded_file_ids
|
|
829
|
+
description: "Using hardcoded file IDs instead of searching or storing dynamically"
|
|
830
|
+
category: data_management
|
|
831
|
+
severity: medium
|
|
832
|
+
wrong: |
|
|
833
|
+
// Hardcoded file ID
|
|
834
|
+
const fileId = '1abc123def456'; // ❌ Breaks across environments
|
|
835
|
+
await update_file({ fileId, ... });
|
|
836
|
+
correct: |
|
|
837
|
+
// Search for file dynamically
|
|
838
|
+
const files = await search_files({ query: 'name="config.json"' });
|
|
839
|
+
const fileId = files[0].id; // ✅ Works across environments
|
|
840
|
+
await update_file({ fileId, ... });
|
|
841
|
+
rationale: "File IDs are environment-specific. Always search or store IDs in config/database."
|
|
842
|
+
|
|
843
|
+
examples:
|
|
844
|
+
create_file:
|
|
845
|
+
- scenario: success
|
|
846
|
+
description: "Create text file in Drive"
|
|
847
|
+
input:
|
|
848
|
+
name: "Project Notes"
|
|
849
|
+
content: "Meeting notes from 2024"
|
|
850
|
+
mimeType: "text/plain"
|
|
851
|
+
folderId: "root"
|
|
852
|
+
output:
|
|
853
|
+
id: "1abc123def456"
|
|
854
|
+
name: "Project Notes"
|
|
855
|
+
webViewLink: "https://drive.google.com/file/d/1abc123def456/view"
|
|
856
|
+
|
|
857
|
+
- scenario: failure_invalid_param
|
|
858
|
+
description: "Missing required name field"
|
|
859
|
+
input:
|
|
860
|
+
content: "Some content"
|
|
861
|
+
error:
|
|
862
|
+
code: VALIDATION_ERROR
|
|
863
|
+
message: "name is required for create_file"
|
|
864
|
+
validator: validate-drive-operations
|
|
865
|
+
|
|
866
|
+
create_event:
|
|
867
|
+
- scenario: success
|
|
868
|
+
description: "Create Calendar event with attendees"
|
|
869
|
+
input:
|
|
870
|
+
summary: "Team Meeting"
|
|
871
|
+
startTime: "2024-01-15T10:00:00-08:00"
|
|
872
|
+
endTime: "2024-01-15T11:00:00-08:00"
|
|
873
|
+
attendees: ["user1@example.com", "user2@example.com"]
|
|
874
|
+
output:
|
|
875
|
+
id: "event123"
|
|
876
|
+
status: "confirmed"
|
|
877
|
+
htmlLink: "https://calendar.google.com/event?eid=event123"
|
|
878
|
+
|
|
879
|
+
- scenario: failure_invalid_param
|
|
880
|
+
description: "Invalid datetime format"
|
|
881
|
+
input:
|
|
882
|
+
summary: "Meeting"
|
|
883
|
+
startTime: "2024-01-15 10:00"
|
|
884
|
+
endTime: "2024-01-15 11:00"
|
|
885
|
+
error:
|
|
886
|
+
code: VALIDATION_ERROR
|
|
887
|
+
message: "startTime must be in RFC3339 format (e.g., '2024-01-01T10:00:00-07:00')"
|
|
888
|
+
validator: validate-calendar-operations
|
|
889
|
+
|
|
890
|
+
send_email:
|
|
891
|
+
- scenario: success
|
|
892
|
+
description: "Send email with Gmail"
|
|
893
|
+
input:
|
|
894
|
+
to: "recipient@example.com"
|
|
895
|
+
subject: "Project Update"
|
|
896
|
+
body: "Here's the latest update..."
|
|
897
|
+
output:
|
|
898
|
+
id: "msg123"
|
|
899
|
+
threadId: "thread456"
|
|
900
|
+
labelIds: ["SENT"]
|
|
901
|
+
|
|
902
|
+
- scenario: failure_invalid_param
|
|
903
|
+
description: "Invalid email address"
|
|
904
|
+
input:
|
|
905
|
+
to: "invalid-email"
|
|
906
|
+
subject: "Test"
|
|
907
|
+
body: "Test message"
|
|
908
|
+
error:
|
|
909
|
+
code: VALIDATION_ERROR
|
|
910
|
+
message: "to must be a valid email address"
|
|
911
|
+
validator: validate-gmail-operations
|
|
912
|
+
|
|
913
|
+
mcp_specific:
|
|
914
|
+
server_command: "npx -y @modelcontextprotocol/server-google-workspace"
|
|
915
|
+
transport: stdio
|
|
916
|
+
environment_variables:
|
|
917
|
+
- name: GOOGLE_WORKSPACE_OAUTH_CLIENT_ID
|
|
918
|
+
required: true
|
|
919
|
+
description: "OAuth 2.0 Client ID from Google Cloud Console"
|
|
920
|
+
- name: GOOGLE_WORKSPACE_OAUTH_CLIENT_SECRET
|
|
921
|
+
required: true
|
|
922
|
+
description: "OAuth 2.0 Client Secret"
|
|
923
|
+
- name: GOOGLE_WORKSPACE_REFRESH_TOKEN
|
|
924
|
+
required: false
|
|
925
|
+
description: "Refresh token for automatic re-authentication"
|
|
926
|
+
health_check:
|
|
927
|
+
method: tool_call
|
|
928
|
+
command: list_files
|
|
929
|
+
expected_response: "Array of file objects or empty array"
|
|
930
|
+
timeout_ms: 5000
|