footprinter-cli 1.0.0rc3__tar.gz → 1.0.1__tar.gz

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 (162) hide show
  1. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/PKG-INFO +66 -25
  2. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/README.md +64 -22
  3. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/__init__.py +10 -0
  4. footprinter_cli-1.0.1/footprinter/cli/doctor.py +247 -0
  5. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/mcp_setup.py +59 -0
  6. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/setup.py +160 -65
  7. footprinter_cli-1.0.1/footprinter/cli/uninstall.py +179 -0
  8. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/browser_indexer.py +14 -2
  9. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/file_scanner.py +38 -8
  10. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/orchestrator.py +5 -1
  11. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/processing.py +24 -0
  12. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/registry.py +1 -0
  13. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter_cli.egg-info/PKG-INFO +66 -25
  14. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter_cli.egg-info/SOURCES.txt +2 -0
  15. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/pyproject.toml +3 -4
  16. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_package_init.py +4 -1
  17. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/LICENSE +0 -0
  18. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/__init__.py +0 -0
  19. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/access.py +0 -0
  20. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/api/__init__.py +0 -0
  21. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/api/db.py +0 -0
  22. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/api/entities.py +0 -0
  23. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/api/search.py +0 -0
  24. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/api/semantic.py +0 -0
  25. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/api/server.py +0 -0
  26. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/api/status.py +0 -0
  27. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/bundled/__init__.py +0 -0
  28. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/bundled/config.example.yaml +0 -0
  29. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/bundled/patterns/context_patterns.yaml +0 -0
  30. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/bundled/patterns/extensions.yaml +0 -0
  31. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/bundled/patterns/filename_patterns.yaml +0 -0
  32. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/bundled/patterns/mime_mappings.yaml +0 -0
  33. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/bundled/patterns/salesforce_rules.yaml +0 -0
  34. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/bundled/patterns/security_patterns.yaml +0 -0
  35. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/__main__.py +0 -0
  36. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/_common.py +0 -0
  37. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/_policy_helpers.py +0 -0
  38. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/_prompt.py +0 -0
  39. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/api_cmd.py +0 -0
  40. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/connect.py +0 -0
  41. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/data.py +0 -0
  42. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/delete.py +0 -0
  43. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/ingest.py +0 -0
  44. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/mcp_cmd.py +0 -0
  45. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/search.py +0 -0
  46. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/search_cmd.py +0 -0
  47. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/status.py +0 -0
  48. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/status_cmd.py +0 -0
  49. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/upsert.py +0 -0
  50. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/vectorize_cmd.py +0 -0
  51. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/cli/view.py +0 -0
  52. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/connectors/__init__.py +0 -0
  53. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/connectors/config_utils.py +0 -0
  54. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/__init__.py +0 -0
  55. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/browser.py +0 -0
  56. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/chats.py +0 -0
  57. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/clients.py +0 -0
  58. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/emails.py +0 -0
  59. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/files.py +0 -0
  60. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/folders.py +0 -0
  61. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/messages.py +0 -0
  62. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/policies.py +0 -0
  63. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/projects.py +0 -0
  64. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/search.py +0 -0
  65. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/sql_utils.py +0 -0
  66. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/status.py +0 -0
  67. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/db/uploads.py +0 -0
  68. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/__init__.py +0 -0
  69. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/adapters/__init__.py +0 -0
  70. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/adapters/browser.py +0 -0
  71. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/adapters/chat.py +0 -0
  72. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/adapters/ingest.py +0 -0
  73. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/adapters/local_files.py +0 -0
  74. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/adapters/local_folders.py +0 -0
  75. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/adapters/protocol.py +0 -0
  76. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/chat_dedup.py +0 -0
  77. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/chat_indexer.py +0 -0
  78. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/chat_parsers/__init__.py +0 -0
  79. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/chat_parsers/chatgpt_parser.py +0 -0
  80. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/chat_parsers/claude_parser.py +0 -0
  81. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/cli.py +0 -0
  82. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/content_extractors.py +0 -0
  83. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/database.py +0 -0
  84. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/db/__init__.py +0 -0
  85. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/db/connector_schema.py +0 -0
  86. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/db/migration.py +0 -0
  87. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/db/schema.py +0 -0
  88. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/db/security.py +0 -0
  89. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/file_indexer.py +0 -0
  90. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/folder_indexer.py +0 -0
  91. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/full_content_extractor.py +0 -0
  92. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/pipe_runner.py +0 -0
  93. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/run_record.py +0 -0
  94. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/ingest/status.py +0 -0
  95. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/__init__.py +0 -0
  96. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/__main__.py +0 -0
  97. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/db.py +0 -0
  98. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/errors.py +0 -0
  99. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/extraction.py +0 -0
  100. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/server.py +0 -0
  101. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/tools/__init__.py +0 -0
  102. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/tools/navigation.py +0 -0
  103. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/tools/read.py +0 -0
  104. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/tools/search.py +0 -0
  105. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/tools/semantic.py +0 -0
  106. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/mcp/tools/status.py +0 -0
  107. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/paths.py +0 -0
  108. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/permissions.py +0 -0
  109. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/semantic/__init__.py +0 -0
  110. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/semantic/chunking.py +0 -0
  111. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/semantic/embeddings.py +0 -0
  112. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/semantic/hybrid_search.py +0 -0
  113. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/semantic/vector_store.py +0 -0
  114. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/__init__.py +0 -0
  115. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/access_service.py +0 -0
  116. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/chat_service.py +0 -0
  117. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/client_service.py +0 -0
  118. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/content_service.py +0 -0
  119. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/email_service.py +0 -0
  120. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/file_service.py +0 -0
  121. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/folder_service.py +0 -0
  122. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/includes.py +0 -0
  123. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/ingest_service.py +0 -0
  124. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/project_service.py +0 -0
  125. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/roles.py +0 -0
  126. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/search_service.py +0 -0
  127. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/semantic_service.py +0 -0
  128. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/status_service.py +0 -0
  129. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/services/visit_service.py +0 -0
  130. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/source_registry.py +0 -0
  131. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/utils/__init__.py +0 -0
  132. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/utils/hash_utils.py +0 -0
  133. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/utils/logging_config.py +0 -0
  134. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/utils/mime.py +0 -0
  135. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/utils/text.py +0 -0
  136. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/utils/time.py +0 -0
  137. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter/visibility.py +0 -0
  138. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter_cli.egg-info/dependency_links.txt +0 -0
  139. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter_cli.egg-info/entry_points.txt +0 -0
  140. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter_cli.egg-info/requires.txt +0 -0
  141. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/footprinter_cli.egg-info/top_level.txt +0 -0
  142. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/setup.cfg +0 -0
  143. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_access_control_bypasses.py +0 -0
  144. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_access_control_docs.py +0 -0
  145. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_access_recalculate.py +0 -0
  146. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_build_status_filter.py +0 -0
  147. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_bundled.py +0 -0
  148. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_e2e_install.py +0 -0
  149. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_e2e_pipeline.py +0 -0
  150. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_edit_recalculate.py +0 -0
  151. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_examples.py +0 -0
  152. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_files_rename.py +0 -0
  153. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_files_surface.py +0 -0
  154. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_inherit_resolution.py +0 -0
  155. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_logging.py +0 -0
  156. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_no_project_root.py +0 -0
  157. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_paths_no_test_marker.py +0 -0
  158. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_pip_install_e2e.py +0 -0
  159. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_prompt_safety.py +0 -0
  160. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_resolver.py +0 -0
  161. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_security_layer.py +0 -0
  162. {footprinter_cli-1.0.0rc3 → footprinter_cli-1.0.1}/tests/test_security_permissions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: footprinter-cli
3
- Version: 1.0.0rc3
3
+ Version: 1.0.1
4
4
  Summary: A local context layer for your files, browser history, chats, and email — searchable, user-owned, MCP-served.
5
5
  Author: SwellCity Group
6
6
  License: MIT
@@ -8,9 +8,8 @@ Project-URL: Homepage, https://github.com/swellcitygroup/footprinter
8
8
  Project-URL: Repository, https://github.com/swellcitygroup/footprinter
9
9
  Project-URL: Issues, https://github.com/swellcitygroup/footprinter/issues
10
10
  Project-URL: Documentation, https://github.com/swellcitygroup/footprinter/blob/main/README.md
11
- Project-URL: Changelog, https://github.com/swellcitygroup/footprinter/blob/main/CHANGELOG.md
12
11
  Keywords: indexer,mcp,metadata,model-context-protocol,file-indexing,sqlite,context-engineering,personal-information-management
13
- Classifier: Development Status :: 4 - Beta
12
+ Classifier: Development Status :: 5 - Production/Stable
14
13
  Classifier: Environment :: Console
15
14
  Classifier: Intended Audience :: Developers
16
15
  Classifier: Intended Audience :: End Users/Desktop
@@ -58,18 +57,62 @@ Requires-Dist: httpx<1.0,>=0.27.0; extra == "dev"
58
57
 
59
58
  Your work lives across a filesystem, a browser, an inbox, a chat history, and whatever other tools you reach for. Footprinter indexes those sources into a single local store, organizes them into the projects and groupings *you* define, and serves the result to AI agents through a governed access layer. You control what the agent can see. Everything stays on your machine.
60
59
 
60
+ ## Prerequisites
61
+
62
+ - **Python 3.11 or newer.** Stock macOS ships with Python 3.9, which won't work — install a newer Python from [python.org](https://www.python.org/downloads/) (recommended) or via `brew install python@3.11`.
63
+ - **macOS 13+** or **Linux**.
64
+ - **Full Disk Access on macOS** for browser history indexing. Grant it to your terminal app under *System Settings → Privacy & Security → Full Disk Access*. `fp setup` will guide you through this when needed.
65
+
61
66
  ## Install
62
67
 
68
+ The fastest path on a clean machine is the install script — it ensures Python 3.11+ is present and installs `footprinter-cli`:
69
+
70
+ ```bash
71
+ # Base install (CLI + MCP + HTTP API)
72
+ curl -fsSL https://raw.githubusercontent.com/swellcitygroup/footprinter/main/scripts/release/install.sh | bash
73
+
74
+ # Full install (adds semantic search + document parsing)
75
+ curl -fsSL https://raw.githubusercontent.com/swellcitygroup/footprinter/main/scripts/release/install-full.sh | bash
76
+ ```
77
+
78
+ If you prefer to manage the install yourself, use **pipx** (recommended) — modern macOS Python ships PEP 668 enabled, which blocks bare `pip install`:
79
+
63
80
  ```bash
64
- pip install footprinter-cli
81
+ brew install pipx
82
+ pipx ensurepath # then restart your terminal
83
+ pipx install footprinter-cli
84
+ pipx install 'footprinter-cli[full]' # with semantic + parse
65
85
  ```
66
86
 
67
- The base install includes the indexing pipeline, CLI, MCP server, HTTP API, and token encryption. Optional extras add more capabilities:
87
+ Inside an existing venv, `pip` works as expected:
88
+
89
+ ```bash
90
+ ./venv/bin/pip install footprinter-cli
91
+ ./venv/bin/pip install 'footprinter-cli[full]'
92
+ ```
93
+
94
+ The base install includes the indexing pipeline, CLI, MCP server, HTTP API, and token encryption. Optional extras add more:
95
+
96
+ | Extra | What it adds |
97
+ |-------|-------------|
98
+ | `[semantic]` | Semantic search via ChromaDB + ONNX embeddings |
99
+ | `[parse]` | PDF, Word, Excel, PowerPoint content extraction |
100
+ | `[full]` | All optional extras (semantic + parse) |
101
+
102
+ > **Privacy note:** The `[semantic]` extra installs ChromaDB. Footprinter initializes
103
+ > the ChromaDB client with `anonymized_telemetry=False`, so no telemetry is sent
104
+ > regardless of which version pip resolves. ChromaDB also removed product telemetry
105
+ > entirely in version 1.5.4. See
106
+ > [Chroma OSS overview](https://docs.trychroma.com/docs/overview/oss) for details.
107
+
108
+ ### Uninstall
109
+
110
+ `fp uninstall` cleans up the MCP entry and user data first, then run the appropriate package uninstall:
68
111
 
69
112
  ```bash
70
- pip install footprinter-cli[full] # All optional extras (semantic + parse)
71
- pip install footprinter-cli[semantic] # Semantic search (ChromaDB + ONNX embeddings)
72
- pip install footprinter-cli[parse] # PDF, Word, Excel, PowerPoint content extraction
113
+ fp uninstall # remove MCP entry + user data
114
+ pipx uninstall footprinter-cli # if you installed via pipx
115
+ ./venv/bin/pip uninstall footprinter-cli # if you installed inside a venv
73
116
  ```
74
117
 
75
118
  ## Quick Start
@@ -81,16 +124,22 @@ fp status # See what's indexed
81
124
  fp search "meeting notes" # Find things
82
125
  ```
83
126
 
84
- **macOS note:** Browser history indexing requires Full Disk Access for your terminal app (System Settings > Privacy & Security > Full Disk Access).
127
+ A few first-run notes:
128
+
129
+ - The first ingest is implicitly full; subsequent runs are incremental. If you change exclusions or add directories after the first run, re-run with `fp ingest --full` so previously skipped files get picked up.
130
+ - With `[semantic]` or `[full]`, the **first ingest downloads ~80MB** of ONNX embedding model weights. It's a one-time cost — subsequent ingests are fast.
131
+ - Keep the directories you want indexed **outside `~/Downloads`** — the default exclusion list skips it.
85
132
 
86
133
  ## Connect to Claude Desktop
87
134
 
88
135
  Footprinter includes an MCP server that gives Claude Desktop (or any MCP client) structured access to your indexed data:
89
136
 
90
137
  ```bash
91
- fp setup mcp # Configure MCP for Claude Desktop
138
+ fp setup mcp --claude # Configure MCP for Claude Desktop
92
139
  ```
93
140
 
141
+ After running this, **fully quit Claude Desktop (Cmd+Q) and relaunch** before the Footprinter tools appear in the conversation tools list. A simple window close isn't enough — the app keeps running in the menu bar.
142
+
94
143
  Once configured, Claude can search your files, browse projects, and find related conversations — through natural language.
95
144
 
96
145
  ## What It Indexes
@@ -104,6 +153,8 @@ Once configured, Claude can search your files, browse projects, and find related
104
153
  | **Documents** | PDF, Word, Excel, PowerPoint content (with `[parse]` extra) |
105
154
  | **Semantic embeddings** | Conceptual similarity across all sources (with `[semantic]` extra) |
106
155
 
156
+ What lands in the database — and when — is controlled by the **content storage tier** you opt into. By default, Footprinter only indexes metadata; it does not read your file content until you explicitly enable it. See [Content Storage](https://github.com/swellcitygroup/footprinter/blob/main/reference/content-storage.md) for the full breakdown.
157
+
107
158
  Additional sources are available through [connector plugins](#connectors).
108
159
 
109
160
  ## CLI Commands
@@ -124,6 +175,8 @@ All commands use the `fp` entry point.
124
175
  | `fp data` | Export data, generate templates, or import metadata corrections |
125
176
  | `fp delete` | Soft-delete a record |
126
177
  | `fp vectorize` | Manage per-record vectorization control |
178
+ | `fp doctor` | Post-install health check (Python version, install location, FDA, MCP wiring) |
179
+ | `fp uninstall` | Remove Footprinter — MCP entry, user data, package |
127
180
 
128
181
  Run `fp <command> --help` for full usage.
129
182
 
@@ -145,23 +198,10 @@ Single-process CLI with optional MCP server. SQLite database. No containers, no
145
198
 
146
199
  Sources are scanned into SQLite with bidirectional links connecting local files to remote backups via content hash matching. Embeddings are generated at ingest time for semantic search. The MCP server exposes indexed data with two-layer access control (visibility + permissions) — you decide what agents can see.
147
200
 
148
- ## Optional Extras
149
-
150
- | Extra | What it adds |
151
- |-------|-------------|
152
- | `[semantic]` | Semantic search via ChromaDB + ONNX embeddings |
153
- | `[parse]` | PDF, Word, Excel, PowerPoint content extraction |
154
- | `[full]` | All optional extras (semantic + parse) |
155
-
156
- > **Privacy note:** The `[semantic]` extra installs ChromaDB, which bundles PostHog analytics.
157
- > ChromaDB collects anonymous usage telemetry by default. Set `ANONYMIZED_TELEMETRY=False`
158
- > in your environment to disable it. See
159
- > [ChromaDB telemetry docs](https://docs.trychroma.com/docs/overview/telemetry) for details.
160
-
161
201
  ## Requirements
162
202
 
163
203
  - Python 3.11+
164
- - macOS or Linux
204
+ - macOS 13+ or Linux
165
205
  - Full Disk Access on macOS (for browser history)
166
206
 
167
207
  ## Documentation
@@ -169,6 +209,7 @@ Sources are scanned into SQLite with bidirectional links connecting local files
169
209
  - [Interfaces](https://github.com/swellcitygroup/footprinter/blob/main/reference/interfaces.md) — CLI commands, MCP tools, Python API
170
210
  - [Data Model](https://github.com/swellcitygroup/footprinter/blob/main/reference/data-model.md) — database schema
171
211
  - [Pipeline](https://github.com/swellcitygroup/footprinter/blob/main/reference/pipeline.md) — indexing stages and configuration
212
+ - [Content Storage](https://github.com/swellcitygroup/footprinter/blob/main/reference/content-storage.md) — metadata vs. snippet vs. full-content tiers
172
213
  - [Access Control](https://github.com/swellcitygroup/footprinter/blob/main/reference/mcp-access-control.md) — MCP security model
173
214
 
174
215
  ## Contributing
@@ -189,7 +230,7 @@ python3 -m venv venv
189
230
  ### Running tests
190
231
 
191
232
  ```bash
192
- pytest tests/ -v --tb=short
233
+ ./venv/bin/pytest tests/ -v --tb=short
193
234
  ```
194
235
 
195
236
  ### Code style
@@ -6,18 +6,62 @@
6
6
 
7
7
  Your work lives across a filesystem, a browser, an inbox, a chat history, and whatever other tools you reach for. Footprinter indexes those sources into a single local store, organizes them into the projects and groupings *you* define, and serves the result to AI agents through a governed access layer. You control what the agent can see. Everything stays on your machine.
8
8
 
9
+ ## Prerequisites
10
+
11
+ - **Python 3.11 or newer.** Stock macOS ships with Python 3.9, which won't work — install a newer Python from [python.org](https://www.python.org/downloads/) (recommended) or via `brew install python@3.11`.
12
+ - **macOS 13+** or **Linux**.
13
+ - **Full Disk Access on macOS** for browser history indexing. Grant it to your terminal app under *System Settings → Privacy & Security → Full Disk Access*. `fp setup` will guide you through this when needed.
14
+
9
15
  ## Install
10
16
 
17
+ The fastest path on a clean machine is the install script — it ensures Python 3.11+ is present and installs `footprinter-cli`:
18
+
19
+ ```bash
20
+ # Base install (CLI + MCP + HTTP API)
21
+ curl -fsSL https://raw.githubusercontent.com/swellcitygroup/footprinter/main/scripts/release/install.sh | bash
22
+
23
+ # Full install (adds semantic search + document parsing)
24
+ curl -fsSL https://raw.githubusercontent.com/swellcitygroup/footprinter/main/scripts/release/install-full.sh | bash
25
+ ```
26
+
27
+ If you prefer to manage the install yourself, use **pipx** (recommended) — modern macOS Python ships PEP 668 enabled, which blocks bare `pip install`:
28
+
11
29
  ```bash
12
- pip install footprinter-cli
30
+ brew install pipx
31
+ pipx ensurepath # then restart your terminal
32
+ pipx install footprinter-cli
33
+ pipx install 'footprinter-cli[full]' # with semantic + parse
13
34
  ```
14
35
 
15
- The base install includes the indexing pipeline, CLI, MCP server, HTTP API, and token encryption. Optional extras add more capabilities:
36
+ Inside an existing venv, `pip` works as expected:
37
+
38
+ ```bash
39
+ ./venv/bin/pip install footprinter-cli
40
+ ./venv/bin/pip install 'footprinter-cli[full]'
41
+ ```
42
+
43
+ The base install includes the indexing pipeline, CLI, MCP server, HTTP API, and token encryption. Optional extras add more:
44
+
45
+ | Extra | What it adds |
46
+ |-------|-------------|
47
+ | `[semantic]` | Semantic search via ChromaDB + ONNX embeddings |
48
+ | `[parse]` | PDF, Word, Excel, PowerPoint content extraction |
49
+ | `[full]` | All optional extras (semantic + parse) |
50
+
51
+ > **Privacy note:** The `[semantic]` extra installs ChromaDB. Footprinter initializes
52
+ > the ChromaDB client with `anonymized_telemetry=False`, so no telemetry is sent
53
+ > regardless of which version pip resolves. ChromaDB also removed product telemetry
54
+ > entirely in version 1.5.4. See
55
+ > [Chroma OSS overview](https://docs.trychroma.com/docs/overview/oss) for details.
56
+
57
+ ### Uninstall
58
+
59
+ `fp uninstall` cleans up the MCP entry and user data first, then run the appropriate package uninstall:
16
60
 
17
61
  ```bash
18
- pip install footprinter-cli[full] # All optional extras (semantic + parse)
19
- pip install footprinter-cli[semantic] # Semantic search (ChromaDB + ONNX embeddings)
20
- pip install footprinter-cli[parse] # PDF, Word, Excel, PowerPoint content extraction
62
+ fp uninstall # remove MCP entry + user data
63
+ pipx uninstall footprinter-cli # if you installed via pipx
64
+ ./venv/bin/pip uninstall footprinter-cli # if you installed inside a venv
21
65
  ```
22
66
 
23
67
  ## Quick Start
@@ -29,16 +73,22 @@ fp status # See what's indexed
29
73
  fp search "meeting notes" # Find things
30
74
  ```
31
75
 
32
- **macOS note:** Browser history indexing requires Full Disk Access for your terminal app (System Settings > Privacy & Security > Full Disk Access).
76
+ A few first-run notes:
77
+
78
+ - The first ingest is implicitly full; subsequent runs are incremental. If you change exclusions or add directories after the first run, re-run with `fp ingest --full` so previously skipped files get picked up.
79
+ - With `[semantic]` or `[full]`, the **first ingest downloads ~80MB** of ONNX embedding model weights. It's a one-time cost — subsequent ingests are fast.
80
+ - Keep the directories you want indexed **outside `~/Downloads`** — the default exclusion list skips it.
33
81
 
34
82
  ## Connect to Claude Desktop
35
83
 
36
84
  Footprinter includes an MCP server that gives Claude Desktop (or any MCP client) structured access to your indexed data:
37
85
 
38
86
  ```bash
39
- fp setup mcp # Configure MCP for Claude Desktop
87
+ fp setup mcp --claude # Configure MCP for Claude Desktop
40
88
  ```
41
89
 
90
+ After running this, **fully quit Claude Desktop (Cmd+Q) and relaunch** before the Footprinter tools appear in the conversation tools list. A simple window close isn't enough — the app keeps running in the menu bar.
91
+
42
92
  Once configured, Claude can search your files, browse projects, and find related conversations — through natural language.
43
93
 
44
94
  ## What It Indexes
@@ -52,6 +102,8 @@ Once configured, Claude can search your files, browse projects, and find related
52
102
  | **Documents** | PDF, Word, Excel, PowerPoint content (with `[parse]` extra) |
53
103
  | **Semantic embeddings** | Conceptual similarity across all sources (with `[semantic]` extra) |
54
104
 
105
+ What lands in the database — and when — is controlled by the **content storage tier** you opt into. By default, Footprinter only indexes metadata; it does not read your file content until you explicitly enable it. See [Content Storage](https://github.com/swellcitygroup/footprinter/blob/main/reference/content-storage.md) for the full breakdown.
106
+
55
107
  Additional sources are available through [connector plugins](#connectors).
56
108
 
57
109
  ## CLI Commands
@@ -72,6 +124,8 @@ All commands use the `fp` entry point.
72
124
  | `fp data` | Export data, generate templates, or import metadata corrections |
73
125
  | `fp delete` | Soft-delete a record |
74
126
  | `fp vectorize` | Manage per-record vectorization control |
127
+ | `fp doctor` | Post-install health check (Python version, install location, FDA, MCP wiring) |
128
+ | `fp uninstall` | Remove Footprinter — MCP entry, user data, package |
75
129
 
76
130
  Run `fp <command> --help` for full usage.
77
131
 
@@ -93,23 +147,10 @@ Single-process CLI with optional MCP server. SQLite database. No containers, no
93
147
 
94
148
  Sources are scanned into SQLite with bidirectional links connecting local files to remote backups via content hash matching. Embeddings are generated at ingest time for semantic search. The MCP server exposes indexed data with two-layer access control (visibility + permissions) — you decide what agents can see.
95
149
 
96
- ## Optional Extras
97
-
98
- | Extra | What it adds |
99
- |-------|-------------|
100
- | `[semantic]` | Semantic search via ChromaDB + ONNX embeddings |
101
- | `[parse]` | PDF, Word, Excel, PowerPoint content extraction |
102
- | `[full]` | All optional extras (semantic + parse) |
103
-
104
- > **Privacy note:** The `[semantic]` extra installs ChromaDB, which bundles PostHog analytics.
105
- > ChromaDB collects anonymous usage telemetry by default. Set `ANONYMIZED_TELEMETRY=False`
106
- > in your environment to disable it. See
107
- > [ChromaDB telemetry docs](https://docs.trychroma.com/docs/overview/telemetry) for details.
108
-
109
150
  ## Requirements
110
151
 
111
152
  - Python 3.11+
112
- - macOS or Linux
153
+ - macOS 13+ or Linux
113
154
  - Full Disk Access on macOS (for browser history)
114
155
 
115
156
  ## Documentation
@@ -117,6 +158,7 @@ Sources are scanned into SQLite with bidirectional links connecting local files
117
158
  - [Interfaces](https://github.com/swellcitygroup/footprinter/blob/main/reference/interfaces.md) — CLI commands, MCP tools, Python API
118
159
  - [Data Model](https://github.com/swellcitygroup/footprinter/blob/main/reference/data-model.md) — database schema
119
160
  - [Pipeline](https://github.com/swellcitygroup/footprinter/blob/main/reference/pipeline.md) — indexing stages and configuration
161
+ - [Content Storage](https://github.com/swellcitygroup/footprinter/blob/main/reference/content-storage.md) — metadata vs. snippet vs. full-content tiers
120
162
  - [Access Control](https://github.com/swellcitygroup/footprinter/blob/main/reference/mcp-access-control.md) — MCP security model
121
163
 
122
164
  ## Contributing
@@ -137,7 +179,7 @@ python3 -m venv venv
137
179
  ### Running tests
138
180
 
139
181
  ```bash
140
- pytest tests/ -v --tb=short
182
+ ./venv/bin/pytest tests/ -v --tb=short
141
183
  ```
142
184
 
143
185
  ### Code style
@@ -63,6 +63,12 @@ def main(argv=None) -> None:
63
63
  " fp api Start the HTTP API server\n"
64
64
  " fp mcp check ~/Work/file Check access resolution for a path\n"
65
65
  "\n"
66
+ "diagnostics:\n"
67
+ " fp doctor Check installation health\n"
68
+ "\n"
69
+ "cleanup:\n"
70
+ " fp uninstall Remove Footprinter (MCP entry, data, package)\n"
71
+ "\n"
66
72
  "tip: use 'fp <command> --help' for details on any command."
67
73
  ),
68
74
  formatter_class=FORMATTER,
@@ -80,11 +86,13 @@ def main(argv=None) -> None:
80
86
  connect,
81
87
  data,
82
88
  delete,
89
+ doctor,
83
90
  ingest,
84
91
  mcp_cmd,
85
92
  search_cmd,
86
93
  setup,
87
94
  status_cmd,
95
+ uninstall,
88
96
  upsert,
89
97
  vectorize_cmd,
90
98
  view,
@@ -103,6 +111,8 @@ def main(argv=None) -> None:
103
111
  data,
104
112
  delete,
105
113
  vectorize_cmd,
114
+ uninstall,
115
+ doctor,
106
116
  ]:
107
117
  mod.register(subparsers)
108
118
 
@@ -0,0 +1,247 @@
1
+ """fp doctor — post-install health check command.
2
+
3
+ Diagnostic checks for installation and environment issues.
4
+ Like ``brew doctor`` or ``gh auth status`` — reports problems,
5
+ doesn't fix them.
6
+ """
7
+
8
+ import importlib.util
9
+ import json
10
+ import platform
11
+ import shutil
12
+ import sqlite3
13
+ import sys
14
+ from dataclasses import asdict, dataclass
15
+ from pathlib import Path
16
+
17
+ from footprinter.cli._common import FORMATTER, add_json_flag, console, output_json
18
+
19
+
20
+ @dataclass
21
+ class Check:
22
+ """Result of a single diagnostic check."""
23
+
24
+ name: str
25
+ status: str # OK, WARN, FAIL
26
+ message: str = ""
27
+
28
+
29
+ def _get_python_version() -> tuple:
30
+ return sys.version_info[:3]
31
+
32
+
33
+ def _find_spec(name: str):
34
+ try:
35
+ return importlib.util.find_spec(name)
36
+ except (ModuleNotFoundError, ValueError):
37
+ return None
38
+
39
+
40
+ def _probe_fda() -> bool:
41
+ """Return True if Safari History.db is readable (macOS FDA check)."""
42
+ safari_db = Path.home() / "Library" / "Safari" / "History.db"
43
+ if not safari_db.exists():
44
+ return True # no Safari DB means nothing to check
45
+ try:
46
+ with sqlite3.connect(f"file:{safari_db}?mode=ro", uri=True) as conn:
47
+ conn.execute("SELECT count(*) FROM sqlite_master")
48
+ return True
49
+ except Exception:
50
+ return False
51
+
52
+
53
+ def _check_python_version() -> Check:
54
+ major, minor, micro = _get_python_version()
55
+ if (major, minor) >= (3, 11):
56
+ return Check("python_version", "OK", f"Python {major}.{minor}.{micro}")
57
+ return Check(
58
+ "python_version",
59
+ "FAIL",
60
+ f"Python {major}.{minor}.{micro} — requires 3.11 or later",
61
+ )
62
+
63
+
64
+ def _check_platform() -> Check:
65
+ system = platform.system()
66
+ machine = platform.machine()
67
+ return Check("platform", "OK", f"{system} ({machine})")
68
+
69
+
70
+ def _check_config() -> Check:
71
+ from footprinter.paths import get_config_path
72
+
73
+ config_path = get_config_path()
74
+ if not config_path.exists():
75
+ return Check(
76
+ "config",
77
+ "WARN",
78
+ f"No config at {config_path} — run 'fp setup' to create one",
79
+ )
80
+ try:
81
+ import yaml
82
+
83
+ with open(config_path) as f:
84
+ yaml.safe_load(f)
85
+ return Check("config", "OK", str(config_path))
86
+ except Exception as e:
87
+ return Check("config", "FAIL", f"Config at {config_path} is not valid YAML: {e}")
88
+
89
+
90
+ def _check_database() -> Check:
91
+ from footprinter.paths import get_db_path
92
+
93
+ db_path = get_db_path()
94
+ if not db_path.exists():
95
+ return Check(
96
+ "database",
97
+ "WARN",
98
+ f"No database at {db_path} — run 'fp ingest' to create one",
99
+ )
100
+ try:
101
+ with sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) as conn:
102
+ conn.execute("SELECT count(*) FROM sqlite_master")
103
+ return Check("database", "OK", str(db_path))
104
+ except Exception as e:
105
+ return Check("database", "FAIL", f"Database at {db_path} is not readable: {e}")
106
+
107
+
108
+ def _check_fda() -> Check:
109
+ if platform.system() != "Darwin":
110
+ return Check("fda", "OK", "Not macOS — Full Disk Access not applicable")
111
+ if _probe_fda():
112
+ return Check("fda", "OK", "Safari History.db is readable")
113
+ return Check(
114
+ "fda",
115
+ "WARN",
116
+ "Cannot read Safari History.db — grant Full Disk Access to your terminal in System Settings > Privacy & Security",
117
+ )
118
+
119
+
120
+ def _check_semantic_deps() -> Check:
121
+ missing = []
122
+ for mod in ("chromadb", "sentence_transformers"):
123
+ if _find_spec(mod) is None:
124
+ missing.append(mod)
125
+ if not missing:
126
+ return Check("semantic_deps", "OK", "chromadb and sentence_transformers available")
127
+ return Check(
128
+ "semantic_deps",
129
+ "WARN",
130
+ f"Optional semantic search dependencies not installed: {', '.join(missing)} — install with: pipx install --force 'footprinter-cli[full]'",
131
+ )
132
+
133
+
134
+ def _check_parse_deps() -> Check:
135
+ missing = []
136
+ for mod in ("docx", "pdfplumber"):
137
+ if _find_spec(mod) is None:
138
+ missing.append(mod)
139
+ if not missing:
140
+ return Check("parse_deps", "OK", "Document parsing dependencies available")
141
+ return Check(
142
+ "parse_deps",
143
+ "WARN",
144
+ f"Optional parsing dependencies not installed: {', '.join(missing)} — install with: pipx install --force 'footprinter-cli[full]'",
145
+ )
146
+
147
+
148
+ def _check_mcp_config() -> Check:
149
+ if platform.system() == "Darwin":
150
+ config_path = (
151
+ Path.home()
152
+ / "Library"
153
+ / "Application Support"
154
+ / "Claude"
155
+ / "claude_desktop_config.json"
156
+ )
157
+ else:
158
+ config_dir = Path.home() / ".config" / "Claude"
159
+ config_path = config_dir / "claude_desktop_config.json"
160
+
161
+ if not config_path.exists():
162
+ return Check("mcp_config", "OK", "No Claude Desktop config found (optional)")
163
+
164
+ try:
165
+ with open(config_path) as f:
166
+ data = json.load(f)
167
+ servers = data.get("mcpServers", {})
168
+ if "footprinter" not in servers:
169
+ return Check("mcp_config", "OK", "Claude Desktop config exists but no footprinter entry (optional)")
170
+
171
+ entry = servers["footprinter"]
172
+ command = entry.get("command", "")
173
+ if command:
174
+ resolved = Path(command).exists() if Path(command).is_absolute() else shutil.which(command)
175
+ if not resolved:
176
+ return Check(
177
+ "mcp_config",
178
+ "WARN",
179
+ f"MCP command not found: {command}",
180
+ )
181
+ return Check("mcp_config", "OK", "footprinter MCP server configured in Claude Desktop")
182
+ except Exception as e:
183
+ return Check("mcp_config", "WARN", f"Could not read Claude Desktop config: {e}")
184
+
185
+
186
+ def run_checks() -> list[Check]:
187
+ """Run all diagnostic checks and return the results."""
188
+ return [
189
+ _check_python_version(),
190
+ _check_platform(),
191
+ _check_config(),
192
+ _check_database(),
193
+ _check_fda(),
194
+ _check_semantic_deps(),
195
+ _check_parse_deps(),
196
+ _check_mcp_config(),
197
+ ]
198
+
199
+
200
+ def register(subparsers) -> None:
201
+ """Register ``fp doctor`` on the CLI router."""
202
+ parser = subparsers.add_parser(
203
+ "doctor",
204
+ help="Check installation health",
205
+ description=(
206
+ "Run diagnostic checks on your Footprinter installation.\n"
207
+ "Reports environment, configuration, and dependency status.\n"
208
+ "Diagnostic only — does not modify anything."
209
+ ),
210
+ epilog=(
211
+ "examples:\n"
212
+ " fp doctor Run all health checks\n"
213
+ " fp doctor --json Machine-readable output"
214
+ ),
215
+ formatter_class=FORMATTER,
216
+ )
217
+ add_json_flag(parser)
218
+ parser.set_defaults(func=_handle)
219
+
220
+
221
+ def _handle(args) -> None:
222
+ checks = run_checks()
223
+
224
+ if getattr(args, "json", False):
225
+ output_json([asdict(c) for c in checks])
226
+ else:
227
+ from rich.table import Table
228
+
229
+ table = Table(show_header=False, box=None, pad_edge=False)
230
+ table.add_column("check", style="bold")
231
+ table.add_column("status", width=6)
232
+ table.add_column("message")
233
+
234
+ for c in checks:
235
+ if c.status == "OK":
236
+ status_cell = f"[green]{c.status}[/green]"
237
+ elif c.status == "WARN":
238
+ status_cell = f"[yellow]{c.status}[/yellow]"
239
+ else:
240
+ status_cell = f"[red]{c.status}[/red]"
241
+ table.add_row(c.name, status_cell, c.message)
242
+
243
+ console.print(table)
244
+
245
+ has_fail = any(c.status == "FAIL" for c in checks)
246
+ if has_fail:
247
+ sys.exit(1)
@@ -275,6 +275,65 @@ def write_config(snippet: dict, config_path: Path = None, dry_run: bool = False)
275
275
  return True
276
276
 
277
277
 
278
+ def unregister_mcp_server(config_path: Path = None, dry_run: bool = False) -> bool:
279
+ """Remove the footprinter entry from the Claude Desktop MCP config.
280
+
281
+ Mirrors :func:`write_config` — backs up the existing file before mutating.
282
+ Idempotent: missing file or missing entry both return True without error.
283
+
284
+ Args:
285
+ config_path: Override config path (default: auto-detected).
286
+ dry_run: If True, report intent without writing.
287
+
288
+ Returns:
289
+ True on success (including no-op cases — missing file, missing
290
+ ``mcpServers`` key, or no footprinter entry). False when the
291
+ platform is unsupported (no config path) or the config file
292
+ exists but cannot be parsed as JSON.
293
+ """
294
+ path = config_path or detect_config_path()
295
+
296
+ if path is None:
297
+ console.print("[yellow]Unsupported platform — cannot detect MCP config path.[/yellow]")
298
+ return False
299
+
300
+ if not path.exists():
301
+ console.print(f" [dim]No MCP config at {path} — nothing to remove.[/dim]")
302
+ return True
303
+
304
+ try:
305
+ with open(path, "r") as f:
306
+ existing = json.load(f)
307
+ except (json.JSONDecodeError, OSError) as e:
308
+ console.print(f"[red]Cannot read existing config:[/red] {e}")
309
+ return False
310
+
311
+ # Hand-edited configs may have ``"mcpServers": null`` — coerce to {}.
312
+ servers = existing.get("mcpServers") or {}
313
+ if "footprinter" not in servers:
314
+ console.print(f" [dim]No footprinter entry in {path}.[/dim]")
315
+ return True
316
+
317
+ if dry_run:
318
+ console.print(f"[dim]Would remove footprinter from:[/dim] {path}")
319
+ return True
320
+
321
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
322
+ backup = path.with_suffix(f".backup_{timestamp}.json")
323
+ shutil.copy2(path, backup)
324
+ console.print(f" Backed up to [dim]{backup}[/dim]")
325
+
326
+ del servers["footprinter"]
327
+ existing["mcpServers"] = servers
328
+
329
+ with open(path, "w") as f:
330
+ json.dump(existing, f, indent=2)
331
+ f.write("\n")
332
+
333
+ console.print(f" Removed footprinter from [bold]{path}[/bold]")
334
+ return True
335
+
336
+
278
337
  def print_client_paths():
279
338
  """Render a table of known MCP clients and their config locations."""
280
339
  table = Table(title="MCP Client Config Paths", show_header=True)