synapse-hub 2.2.3 → 2.4.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.
Files changed (317) hide show
  1. package/.agents/agents/failure-analyst.md +11 -0
  2. package/.agents/agents/live-explorer.md +28 -0
  3. package/.agents/agents/orchestrator.md +20 -4
  4. package/.agents/agents/pom-crafter.md +12 -0
  5. package/.agents/agents/quality-gatekeeper.md +19 -4
  6. package/.agents/agents/test-runner.md +11 -0
  7. package/.agents/rules/communication-style.md +40 -0
  8. package/.agents/rules/context-engineering.md +23 -0
  9. package/.agents/rules/git-push-protocol.md +11 -17
  10. package/.agents/rules/graphify.md +14 -14
  11. package/.agents/rules/lessons-learned.md +3 -3
  12. package/.agents/rules/mandatory-playwright-live.md +29 -29
  13. package/.agents/rules/mobile_appium_rules.md +132 -0
  14. package/.agents/rules/performance_testing_rules.md +74 -0
  15. package/.agents/rules/skill-precedence.md +1 -1
  16. package/.agents/rules/systematic-debugging.md +6 -6
  17. package/.agents/skills/agent-orchestration-multi-agent-optimize/SKILL.md +64 -215
  18. package/.agents/skills/agent-orchestration-multi-agent-optimize/examples/swarm_workload_config.json +12 -0
  19. package/.agents/skills/antigravity-test-sentinel/SKILL.md +3 -3
  20. package/.agents/skills/antigravity-test-sentinel/examples/dod_gate_policy.json +11 -0
  21. package/.agents/skills/api-network-mocking-interception/SKILL.md +1 -1
  22. package/.agents/skills/api-network-mocking-interception/examples/network_route_stub.json +9 -0
  23. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +9 -9
  24. package/.agents/skills/api-spec-to-e2e-matrix/examples/sample_openapi.json +19 -0
  25. package/.agents/skills/architecture-decision-records/SKILL.md +35 -412
  26. package/.agents/skills/architecture-decision-records/examples/0001-record-architecture-decisions.md +12 -0
  27. package/.agents/skills/auth-session-and-cookie-manager/SKILL.md +4 -1
  28. package/.agents/skills/auth-session-and-cookie-manager/examples/storage_state_sample.json +11 -0
  29. package/.agents/skills/backend-load-and-stress-patterns/SKILL.md +80 -186
  30. package/.agents/skills/backend-load-and-stress-patterns/examples/k6_load_test_sample.js +13 -0
  31. package/.agents/skills/bats-testing-patterns/SKILL.md +1 -1
  32. package/.agents/skills/bats-testing-patterns/examples/cli_runner_test.bats +6 -0
  33. package/.agents/skills/bats-testing-patterns/resources/implementation-playbook.md +614 -614
  34. package/.agents/skills/clean-pom-crafter/SKILL.md +21 -3
  35. package/.agents/skills/clean-pom-crafter/examples/standard_test_case.groovy +28 -28
  36. package/.agents/skills/clean-pom-crafter/references/pom_patterns.groovy +138 -138
  37. package/.agents/skills/code-refactoring-refactor-clean/SKILL.md +2 -2
  38. package/.agents/skills/code-refactoring-refactor-clean/examples/refactored_page_sample.groovy +9 -0
  39. package/.agents/skills/code-refactoring-refactor-clean/resources/implementation-playbook.md +9 -7
  40. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +5 -5
  41. package/.agents/skills/db-state-verifier-and-seeder/examples/db_fixture_seed.json +12 -0
  42. package/.agents/skills/e2e-testing-patterns/SKILL.md +1 -1
  43. package/.agents/skills/e2e-testing-patterns/examples/playwright_sharded_config.ts +12 -0
  44. package/.agents/skills/e2e-testing-patterns/resources/implementation-playbook.md +522 -522
  45. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +17 -3
  46. package/.agents/skills/failure-autopsy-and-healing/examples/sample_junit_failure.xml +8 -0
  47. package/.agents/skills/failure-autopsy-and-healing/references/healing_matrix.md +64 -64
  48. package/.agents/skills/failure-autopsy-and-healing/references/qa_investigation_protocol.md +56 -56
  49. package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +98 -98
  50. package/.agents/skills/file-and-report-validator/SKILL.md +1 -1
  51. package/.agents/skills/file-and-report-validator/examples/sample_export_schema.json +14 -0
  52. package/.agents/skills/flaky-test-sentinel-and-quarantine/SKILL.md +3 -3
  53. package/.agents/skills/flaky-test-sentinel-and-quarantine/examples/quarantine_registry.json +11 -0
  54. package/.agents/skills/gherkin-to-suite/SKILL.md +103 -0
  55. package/.agents/skills/gherkin-to-suite/examples/checkout.feature +39 -0
  56. package/.agents/skills/gherkin-to-suite/examples/suite_report_sample.json +334 -0
  57. package/.agents/skills/github-actions-templates/SKILL.md +47 -304
  58. package/.agents/skills/github-actions-templates/examples/matrix_test_pipeline.yml +8 -0
  59. package/.agents/skills/graphify-impact-analyzer/SKILL.md +6 -6
  60. package/.agents/skills/graphify-impact-analyzer/examples/dependency_blast_radius.json +8 -0
  61. package/.agents/skills/katalon-cli-runner/SKILL.md +2 -2
  62. package/.agents/skills/katalon-cli-runner/examples/headless_kre_exec.sh +3 -0
  63. package/.agents/skills/katalon-cli-runner/references/groovy-patterns.md +209 -209
  64. package/.agents/skills/katalon-cli-runner/references/katalon-api.md +155 -155
  65. package/.agents/skills/katalon-cli-runner/references/runner_commands.md +67 -67
  66. package/.agents/skills/lam-script-generator/SKILL.md +42 -1
  67. package/.agents/skills/lam-script-generator/examples/user_story_sample.md +8 -0
  68. package/.agents/skills/lean-code-ladder/SKILL.md +83 -0
  69. package/.agents/skills/lean-code-ladder/examples/ladder_walkthrough.md +37 -0
  70. package/.agents/skills/meta-prompt-crafter/SKILL.md +107 -0
  71. package/.agents/skills/meta-prompt-crafter/examples/qa_prompt_template.md +35 -0
  72. package/.agents/skills/persistent-planning-files/SKILL.md +108 -0
  73. package/.agents/skills/persistent-planning-files/examples/task_plan_example.md +56 -0
  74. package/.agents/skills/playwright-live-interaction/SKILL.md +49 -1
  75. package/.agents/skills/playwright-live-interaction/examples/live_probe_script.js +5 -0
  76. package/.agents/skills/playwright-live-interaction/references/advanced_superpowers.md +160 -160
  77. package/.agents/skills/playwright-live-interaction/references/compact_accessibility_snapshots.md +75 -75
  78. package/.agents/skills/playwright-live-interaction/references/recipes.md +388 -388
  79. package/.agents/skills/playwright-live-interaction/references/swarm_orchestration.md +134 -134
  80. package/.agents/skills/playwright-webapp-testing/SKILL.md +93 -0
  81. package/.agents/skills/playwright-webapp-testing/examples/server_lifecycle_playwright.py +63 -0
  82. package/.agents/skills/rbt-manual-testing/SKILL.md +150 -0
  83. package/.agents/skills/rbt-manual-testing/examples/checkout_rbt_manual_suite.md +25 -0
  84. package/.agents/skills/security-dast-sentinel/SKILL.md +57 -135
  85. package/.agents/skills/security-dast-sentinel/examples/dast_scan_config.json +10 -0
  86. package/.agents/skills/self-healing-locator/SKILL.md +24 -10
  87. package/.agents/skills/self-healing-locator/examples/aom_diff_sample.json +16 -0
  88. package/.agents/skills/skillspector-security-guard/SKILL.md +32 -90
  89. package/.agents/skills/skillspector-security-guard/examples/safe_mcp_config.json +12 -0
  90. package/.agents/skills/surgical-coding-principles/SKILL.md +89 -0
  91. package/.agents/skills/surgical-coding-principles/examples/surgical_vs_scope_creep.md +46 -0
  92. package/.agents/skills/synthetic-test-data-generator/SKILL.md +3 -3
  93. package/.agents/skills/synthetic-test-data-generator/examples/idempotent_data_recipe.json +9 -0
  94. package/.agents/skills/systematic-debugging/SKILL.md +100 -0
  95. package/.agents/skills/systematic-debugging/examples/four_phase_case.md +44 -0
  96. package/.agents/skills/test-driven-development/SKILL.md +114 -0
  97. package/.agents/skills/test-driven-development/examples/red_green_refactor_qa.md +78 -0
  98. package/.agents/skills/token-and-context-optimizer/SKILL.md +1 -1
  99. package/.agents/skills/token-and-context-optimizer/examples/distilled_log_sample.txt +3 -0
  100. package/.agents/skills/verification-before-completion/SKILL.md +100 -0
  101. package/.agents/skills/verification-before-completion/examples/evidence_log_example.md +46 -0
  102. package/.agents/skills/visual-regression-sentinel/SKILL.md +21 -1
  103. package/.agents/skills/visual-regression-sentinel/examples/visual_mask_rules.json +9 -0
  104. package/.agents/skills/wcag-audit-patterns/SKILL.md +3 -3
  105. package/.agents/skills/wcag-audit-patterns/examples/wcag_compliance_checklist.json +10 -0
  106. package/.agents/skills/wcag-audit-patterns/resources/implementation-playbook.md +541 -541
  107. package/.agents/skills/web-vitals-and-performance-profiler/SKILL.md +1 -1
  108. package/.agents/skills/web-vitals-and-performance-profiler/examples/cwv_budget_policy.json +6 -0
  109. package/.agents/templates/completion_report.md +57 -57
  110. package/.agents/workflows/analyze_requirement_document.md +18 -0
  111. package/.agents/workflows/export_testcases_excel.md +32 -0
  112. package/.agents/workflows/generate_combinatorial_test_data.md +28 -0
  113. package/.agents/workflows/generate_cross_module_test_plan.md +37 -0
  114. package/.agents/workflows/generate_manual_testcases_rbt.md +56 -0
  115. package/.agents/workflows/generate_requirements_from_website.md +25 -0
  116. package/.agents/workflows/generate_testcases_from_requirements.md +21 -0
  117. package/.agents/workflows/graphify.md +10 -10
  118. package/.agents/workflows/verify.md +2 -2
  119. package/.cursor/rules/language.mdc +7 -7
  120. package/.cursor/rules/synapse-qa.mdc +23 -0
  121. package/.cursorrules +7 -0
  122. package/.pre-commit-hooks.yaml +14 -0
  123. package/.windsurfrules +7 -0
  124. package/AGENTS.md +54 -15
  125. package/CLAUDE.md +35 -12
  126. package/LICENSE +30 -0
  127. package/NOTICE +180 -0
  128. package/README.en.md +325 -0
  129. package/README.md +247 -512
  130. package/action.yml +51 -0
  131. package/benchmarks/qa_eval/README.md +71 -0
  132. package/benchmarks/qa_eval/build_dataset.py +677 -0
  133. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-06_api_contract_drift.json +22 -0
  134. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-07_api_contract_drift.json +22 -0
  135. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-08_api_contract_drift.json +22 -0
  136. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-09_api_contract_drift.json +22 -0
  137. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-10_api_contract_drift.json +22 -0
  138. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-21_fake_pass_tautology.json +21 -0
  139. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-22_fake_pass_tautology.json +21 -0
  140. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-23_fake_pass_tautology.json +21 -0
  141. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-24_fake_pass_tautology.json +21 -0
  142. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-25_fake_pass_tautology.json +21 -0
  143. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-01_locator_drift.json +23 -0
  144. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-02_locator_drift.json +23 -0
  145. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-03_locator_drift.json +23 -0
  146. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-04_locator_drift.json +23 -0
  147. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-05_locator_drift.json +23 -0
  148. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-16_stale_and_mutation.json +22 -0
  149. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-17_stale_and_mutation.json +22 -0
  150. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-18_stale_and_mutation.json +22 -0
  151. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-19_stale_and_mutation.json +22 -0
  152. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-20_stale_and_mutation.json +23 -0
  153. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-11_timing_and_race.json +22 -0
  154. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-12_timing_and_race.json +22 -0
  155. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-13_timing_and_race.json +22 -0
  156. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-14_timing_and_race.json +22 -0
  157. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-15_timing_and_race.json +22 -0
  158. package/benchmarks/qa_eval/eval_runner.py +418 -0
  159. package/benchmarks/qa_eval/reports/AB_METHODOLOGY_REPORT.md +83 -0
  160. package/benchmarks/qa_eval/reports/BENCHMARK_REPORT.md +69 -0
  161. package/benchmarks/qa_eval/reports/eval_results.json +105 -0
  162. package/bin/agent-qa.js +10 -3
  163. package/docs/ARCHITECTURE.md +107 -0
  164. package/docs/CI_CD_INTEGRATION.md +94 -0
  165. package/docs/CLI_REFERENCE.md +157 -0
  166. package/docs/CONTRIBUTING.md +91 -0
  167. package/docs/PLUGIN_DEVELOPMENT_GUIDE.md +126 -0
  168. package/docs/TOKEN_QUOTA_PLAYBOOK.md +41 -0
  169. package/docs/adopted-ecosystem-tools.md +87 -0
  170. package/docs/schemas/synapse.config.schema.json +64 -0
  171. package/examples/cypress-ecommerce-suite/README.md +45 -0
  172. package/examples/cypress-ecommerce-suite/cypress/e2e/checkout.cy.js +17 -0
  173. package/examples/cypress-ecommerce-suite/cypress/pages/BasePage.js +29 -0
  174. package/examples/cypress-ecommerce-suite/cypress/pages/CartPage.js +51 -0
  175. package/examples/cypress-ecommerce-suite/cypress.config.js +13 -0
  176. package/examples/cypress-ecommerce-suite/package.json +13 -0
  177. package/examples/katalon-suite/Keywords/pages/BasePage.groovy +19 -0
  178. package/examples/katalon-suite/Keywords/pages/DashboardPage.groovy +29 -0
  179. package/examples/katalon-suite/Keywords/pages/LoginPage.groovy +45 -0
  180. package/examples/katalon-suite/Profiles/default.glbl +12 -0
  181. package/examples/katalon-suite/README.md +18 -0
  182. package/examples/katalon-suite/Scripts/Auth/TC_LoginSuccess/Script1234567890.groovy +12 -0
  183. package/examples/katalon-suite/Scripts/Dashboard/TC_ViewSummary/Script1234567891.groovy +15 -0
  184. package/examples/katalon-suite/katalon_suite.prj +21 -0
  185. package/examples/playwright-crm-suite/README.md +40 -0
  186. package/examples/playwright-crm-suite/package.json +14 -0
  187. package/examples/playwright-crm-suite/pages/BasePage.ts +37 -0
  188. package/examples/playwright-crm-suite/pages/CustomerPage.ts +57 -0
  189. package/examples/playwright-crm-suite/pages/LoginPage.ts +32 -0
  190. package/examples/playwright-crm-suite/playwright.config.ts +27 -0
  191. package/examples/playwright-crm-suite/tests/auth.spec.ts +22 -0
  192. package/examples/playwright-crm-suite/tests/customers.spec.ts +33 -0
  193. package/examples/plugins/sample-custom-plugin/README.md +14 -0
  194. package/examples/plugins/sample-custom-plugin/plugin.json +16 -0
  195. package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/SKILL.md +29 -0
  196. package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/examples/audit_fixture.json +8 -0
  197. package/examples/plugins/sample-custom-plugin/tools/custom_security_headers.py +125 -0
  198. package/examples/practices/README.md +22 -0
  199. package/examples/practices/requirements/crm/requirements_contracts.md +292 -0
  200. package/examples/practices/requirements/crm/requirements_customers.md +462 -0
  201. package/examples/practices/requirements/crm/requirements_login.md +202 -0
  202. package/examples/practices/requirements/crm/requirements_projects.md +396 -0
  203. package/examples/practices/requirements/crm/requirements_tasks.md +343 -0
  204. package/examples/practices/requirements/ecommerce/requirements_category_brand.txt +402 -0
  205. package/examples/practices/requirements/jira/KAN-5/KAN-1/2026-04-03_04-28-27.png +0 -0
  206. package/examples/practices/requirements/jira/KAN-5/KAN-1/KAN-1_requirement.md +52 -0
  207. package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-19.png +0 -0
  208. package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-57.png +0 -0
  209. package/examples/practices/requirements/jira/KAN-5/KAN-4/KAN-4_requirement.md +119 -0
  210. package/examples/practices/requirements/jira/KAN-5/KAN-5_overview.md +9 -0
  211. package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-08.png +0 -0
  212. package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-23.png +0 -0
  213. package/examples/practices/requirements/jira/KAN-5/KAN-6/KAN-6_requirement.md +32 -0
  214. package/examples/practices/testcases/api/book_management_api_test_cases.md +144 -0
  215. package/examples/practices/testcases/api/user_management_api_test_cases.md +139 -0
  216. package/examples/practices/testcases/crm/test_cases_crm_login.md +148 -0
  217. package/examples/practices/testcases/crm/test_cases_customers.md +156 -0
  218. package/examples/pytest-api-suite/README.md +45 -0
  219. package/examples/pytest-api-suite/conftest.py +31 -0
  220. package/examples/pytest-api-suite/pages/user_api_client.py +36 -0
  221. package/examples/pytest-api-suite/requirements.txt +2 -0
  222. package/examples/pytest-api-suite/tests/test_user_api.py +44 -0
  223. package/examples/selenium-suite/README.md +18 -0
  224. package/examples/selenium-suite/pages/base_page.py +27 -0
  225. package/examples/selenium-suite/pages/login_page.py +29 -0
  226. package/examples/selenium-suite/pages/order_page.py +25 -0
  227. package/examples/selenium-suite/pytest.ini +6 -0
  228. package/examples/selenium-suite/requirements.txt +2 -0
  229. package/examples/selenium-suite/tests/test_login.py +10 -0
  230. package/examples/selenium-suite/tests/test_order.py +10 -0
  231. package/package.json +22 -7
  232. package/scripts/generate_skill_examples.py +244 -0
  233. package/scripts/install.ps1 +112 -0
  234. package/scripts/install_global_plugin.ps1 +94 -0
  235. package/scripts/install_global_plugin.sh +49 -0
  236. package/scripts/lint_skills.ps1 +57 -0
  237. package/scripts/package_dist.py +323 -0
  238. package/scripts/setup.sh +59 -0
  239. package/scripts/update_hub.ps1 +19 -0
  240. package/scripts/update_hub.sh +15 -0
  241. package/skills_manifest.json +209 -6
  242. package/synapse.config.json +30 -0
  243. package/templates/PROJECT_AI_GUIDE_TEMPLATE.md +7 -7
  244. package/templates/PROJECT_STATE_TEMPLATE.md +1 -1
  245. package/templates/ai_bridges/CLAUDE.md +33 -16
  246. package/templates/ai_bridges/copilot-instructions.md +14 -21
  247. package/templates/ai_bridges/cursor_synapse_qa.mdc +17 -20
  248. package/templates/ai_bridges/windsurfrules +21 -21
  249. package/templates/ci/github-actions-synapse.yml +71 -4
  250. package/templates/welcome.html.tmpl +1674 -0
  251. package/tools/.infer_baseline.json +1102 -0
  252. package/tools/TOOLS_RUNBOOK.md +92 -130
  253. package/tools/__init__.py +26 -0
  254. package/tools/agent_canary.py +501 -501
  255. package/tools/analyze_project.py +31 -17
  256. package/tools/api_fuzzer.py +278 -6
  257. package/tools/author_integrity.py +6 -6
  258. package/tools/auto_heal.py +1 -1
  259. package/tools/browser_helpers.js +105 -7
  260. package/tools/bug_reporter.py +31 -1
  261. package/tools/bump_version.py +23 -3
  262. package/tools/cache_resolver.py +248 -14
  263. package/tools/check_env.py +55 -14
  264. package/tools/check_version_sync.py +153 -0
  265. package/tools/ci_healing_hook.py +1 -1
  266. package/tools/cli.py +263 -13
  267. package/tools/count_tokens.py +1257 -0
  268. package/tools/dashboard_generator.py +2638 -0
  269. package/tools/db_sandbox.py +198 -20
  270. package/tools/e2e_flow_prober.ps1 +12 -64
  271. package/tools/e2e_flow_prober.py +99 -0
  272. package/tools/e2e_flow_prober.sh +4 -0
  273. package/tools/export_postman_collection.py +29 -7
  274. package/tools/flaky_detector.py +4 -3
  275. package/tools/generate_allure_report.ps1 +12 -29
  276. package/tools/generate_allure_report.py +77 -0
  277. package/tools/generate_allure_report.sh +4 -0
  278. package/tools/generate_jmx_plan.py +299 -68
  279. package/tools/generate_test_reports.ps1 +12 -134
  280. package/tools/generate_test_reports.py +133 -0
  281. package/tools/generate_test_reports.sh +4 -0
  282. package/tools/gherkin_suite.py +758 -0
  283. package/tools/git_pre_commit_guard.py +12 -6
  284. package/tools/goldfish_review.py +366 -0
  285. package/tools/heal_pr_automator.py +1 -1
  286. package/tools/heuristic_locator_matcher.py +170 -21
  287. package/tools/infer_linter.py +612 -70
  288. package/tools/jtl_analyzer.py +1 -1
  289. package/tools/lint_ai_docs.py +64 -0
  290. package/tools/matrix_sharder.py +1 -1
  291. package/tools/mcp_excel_server.ps1 +12 -250
  292. package/tools/mcp_excel_server.py +249 -0
  293. package/tools/mcp_excel_server.sh +4 -0
  294. package/tools/mcp_server.py +368 -144
  295. package/tools/md_to_xlsx.py +307 -0
  296. package/tools/micro_mutator.py +300 -69
  297. package/tools/omni_transpiler.py +2 -2
  298. package/tools/output_formatter.py +128 -0
  299. package/tools/pinpoint_failure.py +88 -88
  300. package/tools/project_state.py +2 -2
  301. package/tools/repo_map.py +1 -1
  302. package/tools/safety_firewall.py +197 -33
  303. package/tools/setup_project.py +294 -30
  304. package/tools/skillspector.py +8 -8
  305. package/tools/spec_drift_sentinel.py +439 -0
  306. package/tools/sync_agent_rules.py +248 -0
  307. package/tools/telemetry_correlator.py +45 -0
  308. package/tools/test_impact.py +51 -12
  309. package/tools/tms_sync_bridge.py +21 -5
  310. package/tools/universal_ir.py +3 -0
  311. package/tools/update_sentinel.py +87 -15
  312. package/tools/verify_all.ps1 +13 -0
  313. package/tools/verify_all.py +463 -79
  314. package/tools/visual_diff_canvas.py +1 -0
  315. package/tools/visual_som_helper.py +1 -1
  316. package/tools/watch_mode.py +305 -0
  317. package/tools/welcome_page.py +291 -0
@@ -362,7 +362,7 @@ def main():
362
362
  parser.add_argument("--sla-p95", type=float, default=None, help="P95 Latency threshold in ms (fails if exceeded).")
363
363
  parser.add_argument("--sla-p99", type=float, default=None, help="P99 Latency threshold in ms (fails if exceeded).")
364
364
  parser.add_argument("--sla-avg", type=float, default=None, help="Average Latency threshold in ms (fails if exceeded).")
365
- parser.add_argument("--max-error-rate", type=float, default=None, help="Maximum allowed error rate percentage (e.g. 1.0 = 1%).")
365
+ parser.add_argument("--max-error-rate", type=float, default=None, help="Maximum allowed error rate percentage (e.g. 1.0 = 1%%).")
366
366
  parser.add_argument("--markdown", "-m", help="Output path for Markdown summary report.")
367
367
  parser.add_argument("--json", "-j", help="Output path for JSON summary metrics.")
368
368
  parser.add_argument("--telemetry", help="Path to server telemetry CSV (timestamp, cpu_pct, memory_pct, db_pool_pct) for Full-Stack APM correlation.")
@@ -140,6 +140,70 @@ def lint_docs(repo_root):
140
140
  # Ignored tool references (examples, output targets, artifacts)
141
141
  ignored_tool_refs = {"api_matrix.json", "output.json", "report.json", "output.txt", "summary.json"}
142
142
 
143
+ # 6. Hub Mode Metadata & Count Synchronization (Anti-Drift Guard for Skills, Rules, Workflows)
144
+ manifest_path = os.path.join(repo_root, "skills_manifest.json")
145
+ skills_dir = os.path.join(repo_root, ".agents", "skills")
146
+ rules_dir = os.path.join(repo_root, ".agents", "rules")
147
+ workflows_dir = os.path.join(repo_root, ".agents", "workflows")
148
+
149
+ if os.path.exists(manifest_path) and os.path.isdir(skills_dir):
150
+ import json
151
+ try:
152
+ with open(manifest_path, "r", encoding="utf-8") as mf:
153
+ manifest_data = json.load(mf)
154
+ manifest_skill_ids = set(s.get("id") for s in manifest_data.get("skills", []))
155
+ actual_skill_dirs = set(
156
+ d for d in os.listdir(skills_dir)
157
+ if os.path.isdir(os.path.join(skills_dir, d))
158
+ )
159
+ unregistered = actual_skill_dirs - manifest_skill_ids
160
+ if unregistered:
161
+ errors.append(f"[skills_manifest.json] Skills on disk not in manifest: {sorted(unregistered)}")
162
+ ghost_manifest = manifest_skill_ids - actual_skill_dirs
163
+ if ghost_manifest:
164
+ errors.append(f"[skills_manifest.json] Skills in manifest missing on disk: {sorted(ghost_manifest)}")
165
+
166
+ total_declared = manifest_data.get("totalSkills", len(manifest_skill_ids))
167
+ if total_declared != len(actual_skill_dirs):
168
+ errors.append(f"[skills_manifest.json] totalSkills ({total_declared}) does not match actual count ({len(actual_skill_dirs)})")
169
+
170
+ if os.path.isdir(rules_dir):
171
+ actual_rules_count = len([f for f in os.listdir(rules_dir) if f.endswith(".md")])
172
+ else:
173
+ actual_rules_count = 0
174
+
175
+ if os.path.isdir(workflows_dir):
176
+ actual_workflows_count = len([f for f in os.listdir(workflows_dir) if f.endswith(".md")])
177
+ else:
178
+ actual_workflows_count = 0
179
+
180
+ readme_path = os.path.join(repo_root, "README.md")
181
+ if os.path.exists(readme_path):
182
+ with open(readme_path, "r", encoding="utf-8", errors="ignore") as rf:
183
+ readme_text = rf.read()
184
+ m_rules = re.search(r'\((\d+)\s+Core Rules\)', readme_text)
185
+ if m_rules and int(m_rules.group(1)) != actual_rules_count:
186
+ errors.append(f"[README.md] Documented Core Rules count ({m_rules.group(1)}) differs from actual rules on disk ({actual_rules_count})")
187
+
188
+ m_wf = re.search(r'\((\d+)\s+Workflows\)', readme_text)
189
+ if m_wf and int(m_wf.group(1)) != actual_workflows_count:
190
+ errors.append(f"[README.md] Documented Workflows count ({m_wf.group(1)}) differs from actual workflows on disk ({actual_workflows_count})")
191
+
192
+ skill_table_matches = re.findall(r'\|\s*\d+\s*\|\s*`([a-z0-9\-]+)`\s*\|', readme_text)
193
+ for sid in skill_table_matches:
194
+ if sid not in actual_skill_dirs:
195
+ errors.append(f"[README.md] Non-existent skill referenced in skills table: '{sid}'")
196
+
197
+ arch_path = os.path.join(repo_root, "docs", "ARCHITECTURE.md")
198
+ if os.path.exists(arch_path):
199
+ with open(arch_path, "r", encoding="utf-8", errors="ignore") as af:
200
+ arch_text = af.read()
201
+ m_arch_skills = re.search(r'Catalog\["(\d+)\s+Standardized Skills', arch_text)
202
+ if m_arch_skills and int(m_arch_skills.group(1)) != len(actual_skill_dirs):
203
+ errors.append(f"[ARCHITECTURE.md] Documented skills count ({m_arch_skills.group(1)}) differs from actual skills on disk ({len(actual_skill_dirs)})")
204
+ except Exception as e:
205
+ errors.append(f"Failed to validate skills_manifest.json: {e}")
206
+
143
207
  for doc in doc_files:
144
208
  full_path = os.path.join(repo_root, doc)
145
209
  try:
@@ -25,7 +25,7 @@ if hasattr(sys.stdout, 'reconfigure'):
25
25
 
26
26
  SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
27
27
  HUB_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, ".."))
28
- CACHE_DB_PATH = os.path.join(SCRIPT_DIR, "memory_cache.db")
28
+ CACHE_DB_PATH = os.path.expanduser("~/.agent_memory/global_cache.db")
29
29
 
30
30
 
31
31
  def find_test_files(target_dir: str) -> List[str]:
@@ -1,251 +1,13 @@
1
- param()
2
-
3
- [System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null
4
-
5
- function Read-ExcelSheet {
6
- param(
7
- [string]$FilePath,
8
- [string]$SheetName = ""
9
- )
10
-
11
- if ([string]::IsNullOrWhiteSpace($FilePath) -or -not (Test-Path -LiteralPath $FilePath)) {
12
- if ($env:EXCEL_TEST_PATH -and (Test-Path -LiteralPath $env:EXCEL_TEST_PATH)) {
13
- $FilePath = $env:EXCEL_TEST_PATH
14
- } else {
15
- # Search workspace or Downloads for any recent xlsx file
16
- $found = Get-ChildItem -Path "." -Filter "*.xlsx" -Recurse -Depth 2 -ErrorAction SilentlyContinue | Select-Object -First 1
17
- if (-not $found) {
18
- $found = Get-ChildItem -Path "$env:USERPROFILE\Downloads" -Filter "*.xlsx" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
19
- }
20
- if ($found) { $FilePath = $found.FullName }
21
- }
22
- }
23
-
24
- if ([string]::IsNullOrWhiteSpace($FilePath) -or -not (Test-Path -LiteralPath $FilePath)) {
25
- throw "Excel file not found. Please provide a valid file_path."
26
- }
27
-
28
- $fullPath = (Get-Item $FilePath).FullName
29
- $zip = [System.IO.Compression.ZipFile]::OpenRead($fullPath)
30
-
31
- try {
32
- # 1. Read shared strings
33
- $sharedStrings = New-Object System.Collections.Generic.List[string]
34
- $ssEntry = $zip.GetEntry("xl/sharedStrings.xml")
35
- if ($ssEntry) {
36
- $stream = $ssEntry.Open()
37
- $reader = New-Object System.IO.StreamReader($stream, [System.Text.Encoding]::UTF8)
38
- $ssXml = [xml]$reader.ReadToEnd()
39
- $reader.Close()
40
- $stream.Close()
41
- foreach ($si in $ssXml.sst.si) {
42
- $sharedStrings.Add($si.InnerText)
43
- }
44
- }
45
-
46
- # 2. Determine target sheet entry from workbook.xml
47
- $targetSheetEntryName = "xl/worksheets/sheet1.xml"
48
- $wbEntry = $zip.GetEntry("xl/workbook.xml")
49
- if ($wbEntry) {
50
- $stream = $wbEntry.Open()
51
- $reader = New-Object System.IO.StreamReader($stream, [System.Text.Encoding]::UTF8)
52
- $wbXml = [xml]$reader.ReadToEnd()
53
- $reader.Close()
54
- $stream.Close()
55
-
56
- $sheets = $wbXml.workbook.sheets.sheet
57
- $matchedSheetIndex = 1
58
- if ($SheetName -and $sheets) {
59
- $idx = 1
60
- foreach ($s in $sheets) {
61
- if ($s.name -eq $SheetName -or $s.name -like "*$SheetName*") {
62
- $matchedSheetIndex = $idx
63
- break
64
- }
65
- $idx++
66
- }
67
- }
68
- $targetSheetEntryName = "xl/worksheets/sheet$matchedSheetIndex.xml"
69
- }
70
-
71
- # Fallback if entry doesn't exist
72
- $sEntry = $zip.GetEntry($targetSheetEntryName)
73
- if (-not $sEntry) {
74
- $firstSheet = $zip.Entries | Where-Object { $_.FullName -like "xl/worksheets/sheet*.xml" } | Select-Object -First 1
75
- if ($firstSheet) { $sEntry = $firstSheet }
76
- }
77
-
78
- if (-not $sEntry) {
79
- throw "No worksheet found inside Excel archive."
80
- }
81
-
82
- $stream = $sEntry.Open()
83
- $reader = New-Object System.IO.StreamReader($stream, [System.Text.Encoding]::UTF8)
84
- $sXml = [xml]$reader.ReadToEnd()
85
- $reader.Close()
86
- $stream.Close()
87
-
88
- $rows = $sXml.worksheet.sheetData.row
89
- $currentModule = "General"
90
- $testcases = @()
91
-
92
- foreach ($r in $rows) {
93
- $rowNum = [int]$r.r
94
- $cells = @{}
95
- foreach ($c in $r.c) {
96
- $colLetter = $c.r -replace '\d+'
97
- $val = ""
98
- if ($c.v) {
99
- $raw = $c.v
100
- if ($c.t -eq "s") {
101
- $val = $sharedStrings[[int]$raw]
102
- } else {
103
- $val = $raw
104
- }
105
- }
106
- $cells[$colLetter] = $val
107
- }
108
-
109
- $colA = if ($cells.ContainsKey("A")) { $cells["A"].Trim() } else { "" }
110
- $colB = if ($cells.ContainsKey("B")) { $cells["B"].Trim() } else { "" }
111
-
112
- if ($colB -match "PHAN HE|Phan he|PHÂN HỆ|Phân hệ|MODULE|Module") {
113
- $currentModule = $colB.Trim()
114
- } elseif ($colA -match "PHAN HE|Phan he|PHÂN HỆ|Phân hệ|MODULE|Module") {
115
- $currentModule = $colA.Trim()
116
- }
117
-
118
- if ($colA -match '^TC[-_]?\d+') {
119
- $testcases += [PSCustomObject]@{
120
- Row = $rowNum
121
- Module = $currentModule
122
- ID = $colA
123
- Name = if ($cells.ContainsKey("B")) { $cells["B"].Trim() } else { "" }
124
- PreCondition = if ($cells.ContainsKey("C")) { $cells["C"].Trim() } else { "" }
125
- Steps = if ($cells.ContainsKey("D")) { $cells["D"].Trim() } else { "" }
126
- DataTest = if ($cells.ContainsKey("E")) { $cells["E"].Trim() } else { "" }
127
- Expected = if ($cells.ContainsKey("F")) { $cells["F"].Trim() } else { "" }
128
- Progress = if ($cells.ContainsKey("G")) { $cells["G"].Trim() } else { "" }
129
- Status = if ($cells.ContainsKey("H")) { $cells["H"].Trim() } else { "" }
130
- QC = if ($cells.ContainsKey("I")) { $cells["I"].Trim() } else { "" }
131
- }
132
- }
133
- }
134
- return $testcases
135
- } finally {
136
- $zip.Dispose()
137
- }
138
- }
139
-
140
- # Alias for backwards compatibility
141
- function Read-EnterpriseSheet {
142
- param([string]$FilePath)
143
- return Read-ExcelSheet -FilePath $FilePath
144
- }
145
-
146
- # Stdio JSON-RPC Loop
147
- $inputReader = [System.Console]::In
148
- while (($line = $inputReader.ReadLine()) -ne $null) {
149
- if ([string]::IsNullOrWhiteSpace($line)) { continue }
150
- $id = $null
151
- try {
152
- $req = $line | ConvertFrom-Json
153
- $id = $req.id
154
- $method = $req.method
155
-
156
- if ($method -eq "initialize") {
157
- $resp = @{
158
- jsonrpc = "2.0"
159
- id = $id
160
- result = @{
161
- protocolVersion = "2024-11-05"
162
- capabilities = @{ tools = @{} }
163
- serverInfo = @{ name = "excel-qa-mcp-server"; version = "2.2.3" }
164
- }
165
- }
166
- [System.Console]::Out.WriteLine(($resp | ConvertTo-Json -Compress -Depth 10))
167
- } elseif ($method -eq "notifications/initialized") {
168
- } elseif ($method -eq "ping") {
169
- $resp = @{ jsonrpc = "2.0"; id = $id; result = @{} }
170
- [System.Console]::Out.WriteLine(($resp | ConvertTo-Json -Compress))
171
- } elseif ($method -eq "tools/list") {
172
- $resp = @{
173
- jsonrpc = "2.0"
174
- id = $id
175
- result = @{
176
- tools = @(
177
- @{
178
- name = "read_excel_sheet"
179
- description = "Extract test cases and matrix rows from an Excel worksheet."
180
- inputSchema = @{
181
- type = "object"
182
- properties = @{
183
- file_path = @{ type = "string"; description = "Path to Excel file (.xlsx)" }
184
- sheet_name = @{ type = "string"; description = "Worksheet name (defaults to first sheet)" }
185
- }
186
- }
187
- },
188
- @{
189
- name = "read_Enterprise_sheet"
190
- description = "Compatibility alias for read_excel_sheet"
191
- inputSchema = @{
192
- type = "object"
193
- properties = @{
194
- file_path = @{ type = "string"; description = "Path to Excel file (.xlsx)" }
195
- }
196
- }
197
- }
198
- )
199
- }
200
- }
201
- [System.Console]::Out.WriteLine(($resp | ConvertTo-Json -Compress -Depth 10))
202
- } elseif ($method -eq "tools/call") {
203
- $toolName = $req.params.name
204
- if ($toolName -eq "read_excel_sheet" -or $toolName -eq "read_Enterprise_sheet") {
205
- $fPath = $req.params.arguments.file_path
206
- $sName = if ($req.params.arguments.sheet_name) { $req.params.arguments.sheet_name } else { "" }
207
- $tcs = Read-ExcelSheet -FilePath $fPath -SheetName $sName
208
- $textResult = $tcs | ConvertTo-Json -Depth 5
209
- $resp = @{
210
- jsonrpc = "2.0"
211
- id = $id
212
- result = @{
213
- content = @(
214
- @{ type = "text"; text = $textResult }
215
- )
216
- }
217
- }
218
- [System.Console]::Out.WriteLine(($resp | ConvertTo-Json -Compress -Depth 10))
219
- } else {
220
- $resp = @{
221
- jsonrpc = "2.0"
222
- id = $id
223
- error = @{ code = -32601; message = "Tool not found: $toolName" }
224
- }
225
- [System.Console]::Out.WriteLine(($resp | ConvertTo-Json -Compress))
226
- }
227
- } else {
228
- if ($null -ne $id) {
229
- $resp = @{
230
- jsonrpc = "2.0"
231
- id = $id
232
- error = @{ code = -32601; message = "Method not found: $method" }
233
- }
234
- [System.Console]::Out.WriteLine(($resp | ConvertTo-Json -Compress))
235
- }
236
- }
237
- } catch {
238
- $errMsg = $_.Exception.Message
239
- [System.Console]::Error.WriteLine("[MCP Excel Server Error] $errMsg")
240
- $errCode = if ($null -eq $id) { -32700 } else { -32603 }
241
- $resp = @{
242
- jsonrpc = "2.0"
243
- id = $id
244
- error = @{
245
- code = $errCode
246
- message = "MCP Server Exception: $errMsg"
247
- }
248
- }
249
- [System.Console]::Out.WriteLine(($resp | ConvertTo-Json -Compress))
250
- }
1
+ # Enterprise MCP Excel Server Wrapper
2
+ $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
3
+ $pythonCmd = Get-Command python -ErrorAction SilentlyContinue
4
+ if (-not $pythonCmd) { $pythonCmd = Get-Command py -ErrorAction SilentlyContinue }
5
+ if (-not $pythonCmd) { $pythonCmd = Get-Command python3 -ErrorAction SilentlyContinue }
6
+
7
+ if ($pythonCmd) {
8
+ & $pythonCmd.Source "$scriptDir\mcp_excel_server.py" $args
9
+ exit $LASTEXITCODE
10
+ } else {
11
+ Write-Error "Python 3 is required to run mcp_excel_server.py."
12
+ exit 1
251
13
  }
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Enterprise Cross-Platform MCP Excel Server (Zero-Dependency)
4
+ -----------------------------------------------------------
5
+ Implements Model Context Protocol (MCP) JSON-RPC 2.0 stdio server
6
+ for reading and inspecting Microsoft Excel (.xlsx) workbooks.
7
+ Works 100% offline with Python Standard Library (zipfile + xml.etree.ElementTree),
8
+ with transparent fallback to openpyxl if installed.
9
+ """
10
+
11
+ import os
12
+ import sys
13
+ import json
14
+ import zipfile
15
+ import xml.etree.ElementTree as ET
16
+ from typing import Dict, Any, List, Optional
17
+
18
+ # Enforce UTF-8 stdio
19
+ if hasattr(sys.stdout, 'reconfigure'):
20
+ try:
21
+ sys.stdout.reconfigure(encoding='utf-8')
22
+ sys.stderr.reconfigure(encoding='utf-8')
23
+ except Exception:
24
+ pass
25
+
26
+ NS_MAIN = "{http://schemas.openxmlformats.org/spreadsheetml/2006/main}"
27
+ NS_REL = "{http://schemas.openxmlformats.org/officeDocument/2006/relationships}"
28
+
29
+
30
+ def get_shared_strings(z: zipfile.ZipFile) -> List[str]:
31
+ """Reads shared strings table from xl/sharedStrings.xml."""
32
+ try:
33
+ with z.open("xl/sharedStrings.xml") as f:
34
+ tree = ET.parse(f)
35
+ root = tree.getroot()
36
+ strings = []
37
+ for si in root.iter(f"{NS_MAIN}si"):
38
+ # Concatenate all text inside <t> elements
39
+ text = "".join(t.text or "" for t in si.iter(f"{NS_MAIN}t"))
40
+ strings.append(text)
41
+ return strings
42
+ except KeyError:
43
+ return []
44
+
45
+
46
+ def get_sheet_names(z: zipfile.ZipFile) -> List[str]:
47
+ """Retrieves sheet names from xl/workbook.xml."""
48
+ try:
49
+ with z.open("xl/workbook.xml") as f:
50
+ tree = ET.parse(f)
51
+ root = tree.getroot()
52
+ sheets = []
53
+ for sheet in root.iter(f"{NS_MAIN}sheet"):
54
+ name = sheet.attrib.get("name")
55
+ if name:
56
+ sheets.append(name)
57
+ return sheets
58
+ except KeyError:
59
+ return ["Sheet1"]
60
+
61
+
62
+ def read_sheet_rows(xlsx_path: str, sheet_name: Optional[str] = None, max_rows: int = 100) -> List[List[str]]:
63
+ """Reads rows from an Excel .xlsx file using Python standard library."""
64
+ if not os.path.exists(xlsx_path):
65
+ raise FileNotFoundError(f"Excel file not found: {xlsx_path}")
66
+
67
+ with zipfile.ZipFile(xlsx_path, "r") as z:
68
+ shared_strings = get_shared_strings(z)
69
+ sheet_names = get_sheet_names(z)
70
+
71
+ sheet_idx = 1
72
+ if sheet_name and sheet_name in sheet_names:
73
+ sheet_idx = sheet_names.index(sheet_name) + 1
74
+
75
+ sheet_entry = f"xl/worksheets/sheet{sheet_idx}.xml"
76
+ if sheet_entry not in z.namelist():
77
+ sheet_entry = "xl/worksheets/sheet1.xml"
78
+
79
+ with z.open(sheet_entry) as f:
80
+ tree = ET.parse(f)
81
+ root = tree.getroot()
82
+ rows_data = []
83
+
84
+ for row in root.iter(f"{NS_MAIN}row"):
85
+ cells = []
86
+ for c in row.iter(f"{NS_MAIN}c"):
87
+ cell_type = c.attrib.get("t")
88
+ val_elem = c.find(f"{NS_MAIN}v")
89
+ raw_val = val_elem.text if val_elem is not None else ""
90
+
91
+ if cell_type == "s" and raw_val.isdigit():
92
+ idx = int(raw_val)
93
+ val = shared_strings[idx] if idx < len(shared_strings) else raw_val
94
+ elif cell_type == "b":
95
+ val = "TRUE" if raw_val == "1" else "FALSE"
96
+ else:
97
+ val = raw_val
98
+
99
+ cells.append(str(val))
100
+ if any(cells):
101
+ rows_data.append(cells)
102
+ if len(rows_data) >= max_rows:
103
+ break
104
+
105
+ return rows_data
106
+
107
+
108
+ def handle_tool_call(name: str, arguments: Dict[str, Any]) -> str:
109
+ """Executes Excel MCP tool functions."""
110
+ file_path = arguments.get("file_path", "")
111
+ if not file_path or not os.path.exists(file_path):
112
+ # Check environment or search current dir for xlsx
113
+ test_path = os.environ.get("EXCEL_TEST_PATH", "")
114
+ if test_path and os.path.exists(test_path):
115
+ file_path = test_path
116
+ else:
117
+ for root, _, files in os.walk("."):
118
+ for fn in files:
119
+ if fn.endswith(".xlsx") and not fn.startswith("~$"):
120
+ file_path = os.path.join(root, fn)
121
+ break
122
+ if file_path:
123
+ break
124
+
125
+ if not file_path or not os.path.exists(file_path):
126
+ return "Error: Excel file not found. Please provide a valid file_path argument."
127
+
128
+ if name == "list_excel_sheets":
129
+ with zipfile.ZipFile(file_path, "r") as z:
130
+ sheets = get_sheet_names(z)
131
+ return json.dumps({"file": file_path, "sheets": sheets}, indent=2)
132
+
133
+ elif name == "read_excel_sheet":
134
+ sheet_name = arguments.get("sheet_name")
135
+ max_rows = int(arguments.get("max_rows", 50))
136
+ rows = read_sheet_rows(file_path, sheet_name=sheet_name, max_rows=max_rows)
137
+ if not rows:
138
+ return f"Sheet '{sheet_name or 'Sheet1'}' is empty."
139
+
140
+ # Format as Markdown table
141
+ header = rows[0]
142
+ md_lines = [
143
+ "| " + " | ".join(header) + " |",
144
+ "| " + " | ".join(["---"] * len(header)) + " |"
145
+ ]
146
+ for row in rows[1:]:
147
+ padded = row + [""] * (len(header) - len(row))
148
+ md_lines.append("| " + " | ".join(padded[:len(header)]) + " |")
149
+
150
+ return "\n".join(md_lines)
151
+
152
+ raise ValueError(f"Unknown MCP Excel tool: {name}")
153
+
154
+
155
+ MCP_MANIFEST = {
156
+ "tools": [
157
+ {
158
+ "name": "list_excel_sheets",
159
+ "description": "Lists all worksheet names present in a Microsoft Excel (.xlsx) file.",
160
+ "inputSchema": {
161
+ "type": "object",
162
+ "properties": {
163
+ "file_path": {"type": "string", "description": "Absolute or relative path to the .xlsx file"}
164
+ },
165
+ "required": ["file_path"]
166
+ }
167
+ },
168
+ {
169
+ "name": "read_excel_sheet",
170
+ "description": "Reads and displays tabular rows from an Excel worksheet formatted as a Markdown table.",
171
+ "inputSchema": {
172
+ "type": "object",
173
+ "properties": {
174
+ "file_path": {"type": "string", "description": "Path to the .xlsx file"},
175
+ "sheet_name": {"type": "string", "description": "Optional name of the worksheet to read"},
176
+ "max_rows": {"type": "integer", "description": "Maximum number of rows to retrieve (default: 50)"}
177
+ },
178
+ "required": ["file_path"]
179
+ }
180
+ }
181
+ ]
182
+ }
183
+
184
+
185
+ def process_message(msg: Dict[str, Any]) -> Dict[str, Any]:
186
+ """JSON-RPC 2.0 message handler for Excel MCP."""
187
+ msg_id = msg.get("id")
188
+ method = msg.get("method")
189
+
190
+ if method == "initialize":
191
+ return {
192
+ "jsonrpc": "2.0",
193
+ "id": msg_id,
194
+ "result": {
195
+ "protocolVersion": "2024-11-05",
196
+ "capabilities": {"tools": {}},
197
+ "serverInfo": {"name": "synapse-mcp-excel", "version": "2.3.0"}
198
+ }
199
+ }
200
+ elif method == "tools/list":
201
+ return {
202
+ "jsonrpc": "2.0",
203
+ "id": msg_id,
204
+ "result": MCP_MANIFEST
205
+ }
206
+ elif method == "tools/call":
207
+ params = msg.get("params", {})
208
+ tool_name = params.get("name")
209
+ args = params.get("arguments", {})
210
+ try:
211
+ content = handle_tool_call(tool_name, args)
212
+ return {
213
+ "jsonrpc": "2.0",
214
+ "id": msg_id,
215
+ "result": {"content": [{"type": "text", "text": content}]}
216
+ }
217
+ except Exception as e:
218
+ return {
219
+ "jsonrpc": "2.0",
220
+ "id": msg_id,
221
+ "error": {"code": -32603, "message": str(e)}
222
+ }
223
+
224
+ return {
225
+ "jsonrpc": "2.0",
226
+ "id": msg_id,
227
+ "error": {"code": -32601, "message": f"Method not found: {method}"}
228
+ }
229
+
230
+
231
+ def main():
232
+ """Main stdio loop for JSON-RPC 2.0 communication."""
233
+ for line in sys.stdin:
234
+ line = line.strip()
235
+ if not line:
236
+ continue
237
+ try:
238
+ req = json.loads(line)
239
+ resp = process_message(req)
240
+ sys.stdout.write(json.dumps(resp) + "\n")
241
+ sys.stdout.flush()
242
+ except Exception as e:
243
+ err_resp = {"jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": str(e)}}
244
+ sys.stdout.write(json.dumps(err_resp) + "\n")
245
+ sys.stdout.flush()
246
+
247
+
248
+ if __name__ == "__main__":
249
+ main()
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ # Enterprise MCP Excel Server Wrapper
3
+ set -e
4
+ python3 "$(dirname "$0")/mcp_excel_server.py" "$@"