scribe-mcp 2.2__py3-none-any.whl

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 (206) hide show
  1. scribe_mcp/__init__.py +11 -0
  2. scribe_mcp/__main__.py +31 -0
  3. scribe_mcp/bridges/__init__.py +46 -0
  4. scribe_mcp/bridges/api.py +204 -0
  5. scribe_mcp/bridges/examples/__init__.py +10 -0
  6. scribe_mcp/bridges/examples/hello_world_plugin.py +177 -0
  7. scribe_mcp/bridges/health.py +338 -0
  8. scribe_mcp/bridges/hooks.py +210 -0
  9. scribe_mcp/bridges/manifest.py +278 -0
  10. scribe_mcp/bridges/plugin.py +208 -0
  11. scribe_mcp/bridges/policy.py +115 -0
  12. scribe_mcp/bridges/registry.py +355 -0
  13. scribe_mcp/bridges/security.py +101 -0
  14. scribe_mcp/bridges/tools.py +196 -0
  15. scribe_mcp/cli/__init__.py +4 -0
  16. scribe_mcp/cli/main.py +358 -0
  17. scribe_mcp/cli/session_store.py +113 -0
  18. scribe_mcp/config/__init__.py +5 -0
  19. scribe_mcp/config/boundary_rules_schema.json +71 -0
  20. scribe_mcp/config/display_config.py +254 -0
  21. scribe_mcp/config/global_log_config.json +57 -0
  22. scribe_mcp/config/log_config.json +32 -0
  23. scribe_mcp/config/log_config.py +124 -0
  24. scribe_mcp/config/logging.py +48 -0
  25. scribe_mcp/config/mcp_config.json +12 -0
  26. scribe_mcp/config/paths.py +143 -0
  27. scribe_mcp/config/reminder_config.json +79 -0
  28. scribe_mcp/config/reminder_rules.json +196 -0
  29. scribe_mcp/config/repo_config.py +381 -0
  30. scribe_mcp/config/scribe_config_template.yaml +65 -0
  31. scribe_mcp/config/settings.py +236 -0
  32. scribe_mcp/config/vector_config.py +208 -0
  33. scribe_mcp/db/__init__.py +4 -0
  34. scribe_mcp/db/init.sql +210 -0
  35. scribe_mcp/doc_management/__init__.py +1 -0
  36. scribe_mcp/doc_management/actions/__init__.py +19 -0
  37. scribe_mcp/doc_management/actions/append.py +14 -0
  38. scribe_mcp/doc_management/actions/batch.py +73 -0
  39. scribe_mcp/doc_management/actions/create.py +80 -0
  40. scribe_mcp/doc_management/actions/edit.py +336 -0
  41. scribe_mcp/doc_management/actions/query.py +332 -0
  42. scribe_mcp/doc_management/actions/search.py +208 -0
  43. scribe_mcp/doc_management/actions/status.py +14 -0
  44. scribe_mcp/doc_management/change_logger.py +508 -0
  45. scribe_mcp/doc_management/change_rollback.py +639 -0
  46. scribe_mcp/doc_management/cli.py +208 -0
  47. scribe_mcp/doc_management/conflict_resolver.py +463 -0
  48. scribe_mcp/doc_management/diff_visualizer.py +492 -0
  49. scribe_mcp/doc_management/file_watcher.py +384 -0
  50. scribe_mcp/doc_management/healing.py +252 -0
  51. scribe_mcp/doc_management/indexing.py +295 -0
  52. scribe_mcp/doc_management/integrity_verifier.py +508 -0
  53. scribe_mcp/doc_management/manager.py +2980 -0
  54. scribe_mcp/doc_management/performance_monitor.py +554 -0
  55. scribe_mcp/doc_management/preflight.py +86 -0
  56. scribe_mcp/doc_management/runtime.py +576 -0
  57. scribe_mcp/doc_management/special_create.py +583 -0
  58. scribe_mcp/doc_management/special_indexes.py +430 -0
  59. scribe_mcp/doc_management/sync_manager.py +523 -0
  60. scribe_mcp/doc_management/utils.py +198 -0
  61. scribe_mcp/doc_management/validation.py +52 -0
  62. scribe_mcp/plugins/__init__.py +2 -0
  63. scribe_mcp/plugins/registry.py +541 -0
  64. scribe_mcp/plugins/vector_indexer.json +53 -0
  65. scribe_mcp/plugins/vector_indexer.py +996 -0
  66. scribe_mcp/py.typed +1 -0
  67. scribe_mcp/reminders.py +418 -0
  68. scribe_mcp/scripts/__init__.py +0 -0
  69. scribe_mcp/scripts/backfill_log_type.py +118 -0
  70. scribe_mcp/scripts/check_vector_index.py +104 -0
  71. scribe_mcp/scripts/migrate_database.py +314 -0
  72. scribe_mcp/scripts/reindex_docs.py +49 -0
  73. scribe_mcp/scripts/reindex_vector.py +586 -0
  74. scribe_mcp/scripts/scribe.py +362 -0
  75. scribe_mcp/scripts/scribe_admin.py +571 -0
  76. scribe_mcp/scripts/scribe_cli.py +431 -0
  77. scribe_mcp/scripts/scribe_probe.py +404 -0
  78. scribe_mcp/scripts/test_mcp_server.py +168 -0
  79. scribe_mcp/scripts/verify_migration.py +117 -0
  80. scribe_mcp/security/__init__.py +2 -0
  81. scribe_mcp/security/sandbox.py +364 -0
  82. scribe_mcp/server.py +908 -0
  83. scribe_mcp/shared/__init__.py +3 -0
  84. scribe_mcp/shared/base_logging_tool.py +139 -0
  85. scribe_mcp/shared/execution_context.py +244 -0
  86. scribe_mcp/shared/log_enums.py +193 -0
  87. scribe_mcp/shared/logging_utils.py +732 -0
  88. scribe_mcp/shared/project_registry.py +716 -0
  89. scribe_mcp/shared/project_utils.py +153 -0
  90. scribe_mcp/shared/session_utils.py +80 -0
  91. scribe_mcp/shared/tool_runtime.py +395 -0
  92. scribe_mcp/state/__init__.py +6 -0
  93. scribe_mcp/state/agent_identity.py +260 -0
  94. scribe_mcp/state/agent_manager.py +526 -0
  95. scribe_mcp/state/manager.py +407 -0
  96. scribe_mcp/storage/__init__.py +30 -0
  97. scribe_mcp/storage/base.py +400 -0
  98. scribe_mcp/storage/models.py +210 -0
  99. scribe_mcp/storage/pool.py +409 -0
  100. scribe_mcp/storage/postgres.py +260 -0
  101. scribe_mcp/storage/sqlite/__init__.py +433 -0
  102. scribe_mcp/storage/sqlite/compat_migrations.py +200 -0
  103. scribe_mcp/storage/sqlite/documents.py +60 -0
  104. scribe_mcp/storage/sqlite/domain_facade.py +547 -0
  105. scribe_mcp/storage/sqlite/entries.py +451 -0
  106. scribe_mcp/storage/sqlite/internals.py +259 -0
  107. scribe_mcp/storage/sqlite/migrations.py +308 -0
  108. scribe_mcp/storage/sqlite/planning.py +308 -0
  109. scribe_mcp/storage/sqlite/projects.py +251 -0
  110. scribe_mcp/storage/sqlite/schema.py +491 -0
  111. scribe_mcp/storage/sqlite/sessions.py +449 -0
  112. scribe_mcp/storage/sqlite/telemetry.py +398 -0
  113. scribe_mcp/storage/sqlite/telemetry_support.py +275 -0
  114. scribe_mcp/template_engine/__init__.py +11 -0
  115. scribe_mcp/template_engine/cli.py +160 -0
  116. scribe_mcp/template_engine/engine.py +593 -0
  117. scribe_mcp/templates/__init__.py +194 -0
  118. scribe_mcp/templates/__pycache__/__init__.cpython-311.pyc +0 -0
  119. scribe_mcp/templates/custom/project_header.md +51 -0
  120. scribe_mcp/templates/documents/AGENT_REPORT_CARD_TEMPLATE.md +158 -0
  121. scribe_mcp/templates/documents/ARCHITECTURE_GUIDE_TEMPLATE.md +124 -0
  122. scribe_mcp/templates/documents/BUG_LOG_TEMPLATE.md +76 -0
  123. scribe_mcp/templates/documents/BUG_REPORT_TEMPLATE.md +78 -0
  124. scribe_mcp/templates/documents/CHECKLIST_TEMPLATE.md +59 -0
  125. scribe_mcp/templates/documents/DOC_LOG_TEMPLATE.md +48 -0
  126. scribe_mcp/templates/documents/GLOBAL_PROGRESS_LOG_TEMPLATE.md +87 -0
  127. scribe_mcp/templates/documents/PHASE_PLAN_TEMPLATE.md +97 -0
  128. scribe_mcp/templates/documents/PROGRESS_LOG_TEMPLATE.md +38 -0
  129. scribe_mcp/templates/documents/RESEARCH_REPORT_TEMPLATE.md +66 -0
  130. scribe_mcp/templates/documents/REVIEW_REPORT_TEMPLATE.md +135 -0
  131. scribe_mcp/templates/documents/SECURITY_LOG_TEMPLATE.md +60 -0
  132. scribe_mcp/templates/documents/base_document.md +85 -0
  133. scribe_mcp/templates/documents/base_log.md +61 -0
  134. scribe_mcp/templates/fragments/api_endpoints.md +26 -0
  135. scribe_mcp/templates/fragments/directory_structure.md +43 -0
  136. scribe_mcp/templates/fragments/testing_strategy.md +50 -0
  137. scribe_mcp/tools/__init__.py +39 -0
  138. scribe_mcp/tools/agent_project_utils.py +192 -0
  139. scribe_mcp/tools/append_entry.py +2189 -0
  140. scribe_mcp/tools/base/__init__.py +34 -0
  141. scribe_mcp/tools/base/base_tool.py +150 -0
  142. scribe_mcp/tools/base/parameter_normalizer.py +163 -0
  143. scribe_mcp/tools/base/tool_metadata.py +482 -0
  144. scribe_mcp/tools/base/tool_result.py +89 -0
  145. scribe_mcp/tools/config/append_entry_config.py +602 -0
  146. scribe_mcp/tools/config/query_entries_config.py +590 -0
  147. scribe_mcp/tools/config/rotate_log_config.py +494 -0
  148. scribe_mcp/tools/constants.py +11 -0
  149. scribe_mcp/tools/delete_project.py +238 -0
  150. scribe_mcp/tools/doctor.py +113 -0
  151. scribe_mcp/tools/edit_file.py +388 -0
  152. scribe_mcp/tools/generate_doc_templates.py +597 -0
  153. scribe_mcp/tools/get_project.py +649 -0
  154. scribe_mcp/tools/health_check.py +315 -0
  155. scribe_mcp/tools/list_projects.py +734 -0
  156. scribe_mcp/tools/manage_docs.py +244 -0
  157. scribe_mcp/tools/manage_docs_validation.py +287 -0
  158. scribe_mcp/tools/project_utils.py +254 -0
  159. scribe_mcp/tools/query_entries.py +2204 -0
  160. scribe_mcp/tools/read_file.py +2553 -0
  161. scribe_mcp/tools/read_recent.py +591 -0
  162. scribe_mcp/tools/rotate_log.py +2132 -0
  163. scribe_mcp/tools/search.py +919 -0
  164. scribe_mcp/tools/sentinel_tools.py +710 -0
  165. scribe_mcp/tools/set_project.py +963 -0
  166. scribe_mcp/tools/vector_search.py +419 -0
  167. scribe_mcp/utils/__init__.py +26 -0
  168. scribe_mcp/utils/audit.py +403 -0
  169. scribe_mcp/utils/bulk_processor.py +585 -0
  170. scribe_mcp/utils/config_manager.py +1504 -0
  171. scribe_mcp/utils/context_safety.py +293 -0
  172. scribe_mcp/utils/diff_compiler.py +24 -0
  173. scribe_mcp/utils/entry_limit.py +151 -0
  174. scribe_mcp/utils/error_handler.py +1517 -0
  175. scribe_mcp/utils/estimator.py +768 -0
  176. scribe_mcp/utils/files.py +864 -0
  177. scribe_mcp/utils/formatters/__init__.py +37 -0
  178. scribe_mcp/utils/formatters/base.py +323 -0
  179. scribe_mcp/utils/formatters/dispatcher.py +470 -0
  180. scribe_mcp/utils/formatters/entry.py +743 -0
  181. scribe_mcp/utils/formatters/file.py +767 -0
  182. scribe_mcp/utils/formatters/project.py +1022 -0
  183. scribe_mcp/utils/formatters/ui.py +427 -0
  184. scribe_mcp/utils/frontmatter.py +125 -0
  185. scribe_mcp/utils/integrity.py +264 -0
  186. scribe_mcp/utils/logs.py +94 -0
  187. scribe_mcp/utils/parameter_validator.py +1538 -0
  188. scribe_mcp/utils/path_suggestions.py +249 -0
  189. scribe_mcp/utils/path_utils.py +107 -0
  190. scribe_mcp/utils/reminder_engine.py +606 -0
  191. scribe_mcp/utils/reminder_monitoring.py +523 -0
  192. scribe_mcp/utils/reminder_validator.py +285 -0
  193. scribe_mcp/utils/response.py +1365 -0
  194. scribe_mcp/utils/rotation_state.py +532 -0
  195. scribe_mcp/utils/search.py +40 -0
  196. scribe_mcp/utils/sentinel_logs.py +182 -0
  197. scribe_mcp/utils/slug.py +88 -0
  198. scribe_mcp/utils/time.py +80 -0
  199. scribe_mcp/utils/tokens.py +360 -0
  200. scribe_mcp/utils/tool_logger.py +208 -0
  201. scribe_mcp-2.2.dist-info/METADATA +998 -0
  202. scribe_mcp-2.2.dist-info/RECORD +206 -0
  203. scribe_mcp-2.2.dist-info/WHEEL +5 -0
  204. scribe_mcp-2.2.dist-info/entry_points.txt +4 -0
  205. scribe_mcp-2.2.dist-info/licenses/LICENSE +46 -0
  206. scribe_mcp-2.2.dist-info/top_level.txt +1 -0
scribe_mcp/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ """Scribe MCP server package."""
2
+
3
+ __all__ = [
4
+ "config",
5
+ "db",
6
+ "server",
7
+ "state",
8
+ "tools",
9
+ "utils",
10
+ ]
11
+
scribe_mcp/__main__.py ADDED
@@ -0,0 +1,31 @@
1
+ """Package entrypoint for running the Scribe MCP server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ from collections.abc import Sequence
8
+
9
+ from scribe_mcp.server import main as server_main
10
+
11
+
12
+ def _parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
13
+ parser = argparse.ArgumentParser(
14
+ description="Run the Scribe MCP server over stdio.",
15
+ )
16
+ parser.add_argument(
17
+ "--version",
18
+ action="version",
19
+ version="scribe-mcp 2.2",
20
+ )
21
+ return parser.parse_args(argv)
22
+
23
+
24
+ def main(argv: Sequence[str] | None = None) -> None:
25
+ _parse_args(argv)
26
+ asyncio.run(server_main())
27
+
28
+
29
+ if __name__ == "__main__":
30
+ main()
31
+
@@ -0,0 +1,46 @@
1
+ """Bridge registry system for external integrations."""
2
+
3
+ from .manifest import (
4
+ BridgeManifest,
5
+ BridgeState,
6
+ LogTypeConfig,
7
+ HookConfig,
8
+ BridgeProjectConfig,
9
+ BridgeValidationConfig,
10
+ )
11
+ from .plugin import BridgePlugin
12
+ from .registry import BridgeRegistry
13
+ from .api import BridgeToScribeAPI
14
+ from .policy import BridgePolicyPlugin
15
+ from .hooks import BridgeHookManager, get_hook_manager
16
+ from .security import BridgeSecurityManager
17
+ from .tools import BridgeToolWrapper, BridgeToolRegistry, get_tool_registry
18
+ from .health import (
19
+ BridgeHealthMonitor,
20
+ get_health_monitor,
21
+ set_health_monitor,
22
+ create_health_monitor,
23
+ )
24
+
25
+ __all__ = [
26
+ "BridgeManifest",
27
+ "BridgeState",
28
+ "LogTypeConfig",
29
+ "HookConfig",
30
+ "BridgeProjectConfig",
31
+ "BridgeValidationConfig",
32
+ "BridgePlugin",
33
+ "BridgeRegistry",
34
+ "BridgeToScribeAPI",
35
+ "BridgePolicyPlugin",
36
+ "BridgeHookManager",
37
+ "get_hook_manager",
38
+ "BridgeSecurityManager",
39
+ "BridgeToolWrapper",
40
+ "BridgeToolRegistry",
41
+ "get_tool_registry",
42
+ "BridgeHealthMonitor",
43
+ "get_health_monitor",
44
+ "set_health_monitor",
45
+ "create_health_monitor",
46
+ ]
@@ -0,0 +1,204 @@
1
+ """API for bridges to call Scribe operations."""
2
+
3
+ from typing import Dict, List, Any, Optional
4
+ import logging
5
+ from datetime import datetime, timezone
6
+ import uuid
7
+ import hashlib
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class BridgeToScribeAPI:
13
+ """
14
+ API interface for bridge→Scribe calls.
15
+
16
+ All operations enforce permissions via BridgePolicyPlugin
17
+ and inject bridge metadata automatically.
18
+ """
19
+
20
+ def __init__(self, bridge_id: str, manifest, storage_backend, policy):
21
+ self.bridge_id = bridge_id
22
+ self.manifest = manifest
23
+ self._storage = storage_backend
24
+ self._policy = policy
25
+
26
+ async def append_entry(
27
+ self,
28
+ project_name: str,
29
+ message: str,
30
+ status: str = "info",
31
+ agent: Optional[str] = None,
32
+ meta: Optional[Dict[str, Any]] = None,
33
+ log_type: str = "progress"
34
+ ) -> Dict[str, Any]:
35
+ """
36
+ Append entry to Scribe log on behalf of bridge.
37
+
38
+ Enforces permissions and injects bridge metadata.
39
+
40
+ Args:
41
+ project_name: Target project
42
+ message: Log message
43
+ status: Entry status (info, success, warn, error, bug, plan)
44
+ agent: Agent name (defaults to bridge:<bridge_id>)
45
+ meta: Additional metadata
46
+ log_type: Log type (progress, doc_updates, bugs, etc.)
47
+
48
+ Returns:
49
+ Dict with ok=True, entry_id, timestamp
50
+
51
+ Raises:
52
+ PermissionError: If bridge lacks permission
53
+ """
54
+ # Check permission
55
+ if not self._policy.can_append_entry(project_name, log_type):
56
+ raise PermissionError(
57
+ f"Bridge {self.bridge_id} cannot append to {project_name}/{log_type}"
58
+ )
59
+
60
+ # Inject bridge metadata
61
+ meta = meta or {}
62
+ meta["source_bridge_id"] = self.bridge_id
63
+ meta["bridge_version"] = self.manifest.version
64
+
65
+ # Map status to emoji
66
+ emoji_map = {
67
+ "info": "ℹ️",
68
+ "success": "✅",
69
+ "warn": "⚠️",
70
+ "error": "❌",
71
+ "bug": "🐞",
72
+ "plan": "📋"
73
+ }
74
+ emoji = emoji_map.get(status, "ℹ️")
75
+
76
+ # Fetch project record
77
+ project = await self._storage.fetch_project(project_name)
78
+ if not project:
79
+ raise ValueError(f"Project '{project_name}' not found")
80
+
81
+ # Generate entry ID and timestamp
82
+ entry_id = str(uuid.uuid4())[:8]
83
+ ts = datetime.now(timezone.utc)
84
+ timestamp_str = ts.isoformat()
85
+ agent_name = agent or f"bridge:{self.bridge_id}"
86
+
87
+ # Create raw line and compute hash
88
+ raw_line = f"{timestamp_str} | {agent_name} | {message}"
89
+ sha256_hash = hashlib.sha256(raw_line.encode()).hexdigest()
90
+
91
+ # Insert entry via storage backend
92
+ await self._storage.insert_entry(
93
+ entry_id=entry_id,
94
+ project=project,
95
+ ts=ts,
96
+ emoji=emoji,
97
+ agent=agent_name,
98
+ message=message,
99
+ meta=meta,
100
+ raw_line=raw_line,
101
+ sha256=sha256_hash
102
+ )
103
+
104
+ logger.info(f"Bridge {self.bridge_id} appended entry to {project_name}: {message[:50]}")
105
+
106
+ return {
107
+ "ok": True,
108
+ "entry_id": entry_id,
109
+ "timestamp": timestamp_str
110
+ }
111
+
112
+ async def query_entries(
113
+ self,
114
+ project_name: str,
115
+ limit: int = 50,
116
+ **filters
117
+ ) -> List[Dict[str, Any]]:
118
+ """
119
+ Query entries from Scribe log.
120
+
121
+ Args:
122
+ project_name: Target project
123
+ limit: Maximum entries to return
124
+ **filters: Additional query filters
125
+
126
+ Returns:
127
+ List of log entries
128
+
129
+ Raises:
130
+ PermissionError: If bridge lacks permission
131
+ """
132
+ if not self._policy.can_read_entries(project_name):
133
+ raise PermissionError(
134
+ f"Bridge {self.bridge_id} cannot read from {project_name}"
135
+ )
136
+
137
+ entries = await self._storage.fetch_recent_entries(project_name, limit, filters)
138
+ logger.info(f"Bridge {self.bridge_id} queried {len(entries)} entries from {project_name}")
139
+
140
+ return entries
141
+
142
+ async def create_project(
143
+ self,
144
+ name: str,
145
+ description: Optional[str] = None,
146
+ tags: Optional[List[str]] = None,
147
+ meta: Optional[Dict[str, Any]] = None
148
+ ) -> Dict[str, Any]:
149
+ """
150
+ Create a new Scribe project on behalf of bridge.
151
+
152
+ Args:
153
+ name: Project name
154
+ description: Project description
155
+ tags: Project tags
156
+ meta: Additional metadata
157
+
158
+ Returns:
159
+ Dict with ok=True, project_name, tags
160
+
161
+ Raises:
162
+ PermissionError: If bridge lacks permission
163
+ """
164
+ if not self._policy.can_create_projects():
165
+ raise PermissionError(
166
+ f"Bridge {self.bridge_id} cannot create projects"
167
+ )
168
+
169
+ # Apply prefix if configured
170
+ prefix = self.manifest.project_config.project_prefix
171
+ original_name = name
172
+ full_name = f"{prefix}{name}" if prefix else name
173
+
174
+ # Add auto-tags
175
+ all_tags = list(tags or [])
176
+ all_tags.extend(self.manifest.project_config.auto_tag)
177
+ all_tags.append(f"bridge:{self.bridge_id}")
178
+
179
+ # Inject bridge metadata
180
+ project_meta = dict(meta or {})
181
+ project_meta.update(self.manifest.project_config.default_metadata)
182
+ project_meta["managed_by_bridge"] = self.bridge_id
183
+ project_meta["bridge_version"] = self.manifest.version
184
+
185
+ # Create via storage with bridge ownership
186
+ await self._storage.upsert_project(
187
+ name=full_name,
188
+ repo_root=".",
189
+ progress_log_path=f".scribe/docs/dev_plans/{full_name}/PROGRESS_LOG.md",
190
+ docs_json=None,
191
+ bridge_id=self.bridge_id,
192
+ bridge_managed=True
193
+ )
194
+
195
+ logger.info(f"Bridge {self.bridge_id} created project: {full_name}")
196
+
197
+ return {
198
+ "ok": True,
199
+ "project_name": full_name,
200
+ "original_name": original_name,
201
+ "tags": all_tags,
202
+ "bridge_managed": True,
203
+ "bridge_id": self.bridge_id
204
+ }
@@ -0,0 +1,10 @@
1
+ """
2
+ Example bridge plugins for reference and testing.
3
+
4
+ This package contains example implementations of Scribe bridge plugins:
5
+ - hello_world_plugin: Minimal example demonstrating bridge lifecycle
6
+ """
7
+
8
+ from .hello_world_plugin import HelloWorldBridgePlugin, create_plugin
9
+
10
+ __all__ = ["HelloWorldBridgePlugin", "create_plugin"]
@@ -0,0 +1,177 @@
1
+ """
2
+ Hello World Example Bridge Plugin
3
+
4
+ This is a minimal example showing how to create a Scribe bridge plugin.
5
+ Use this as a template for your own bridge implementations.
6
+
7
+ To enable:
8
+ 1. Copy .scribe/config/bridges/hello_world.yaml.example to hello_world.yaml
9
+ 2. Set enabled: true
10
+ 3. Restart the Scribe MCP server
11
+
12
+ Example Usage:
13
+ This plugin is automatically loaded by the BridgeRegistry when enabled.
14
+ It demonstrates:
15
+ - Activation/deactivation lifecycle
16
+ - Health check reporting
17
+ - Pre-append hook for entry modification
18
+ - Post-append hook for event tracking
19
+ """
20
+
21
+ from typing import Any, Dict, Optional
22
+ import logging
23
+
24
+ from bridges.plugin import BridgePlugin
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class HelloWorldBridgePlugin(BridgePlugin):
30
+ """
31
+ Minimal example bridge plugin demonstrating the bridge lifecycle.
32
+
33
+ This plugin:
34
+ - Logs activation/deactivation events
35
+ - Provides a simple health check
36
+ - Demonstrates pre/post append hooks
37
+ - Tracks append events with a counter
38
+
39
+ Attributes:
40
+ bridge_id: Unique identifier for this bridge instance
41
+ manifest: Bridge manifest configuration
42
+ _active: Whether the bridge is currently active
43
+ _append_count: Number of appends processed
44
+ """
45
+
46
+ def __init__(self, manifest: Any):
47
+ """
48
+ Initialize the HelloWorld bridge plugin.
49
+
50
+ Args:
51
+ manifest: Loaded BridgeManifest configuration
52
+ """
53
+ super().__init__(manifest)
54
+ self._active = False
55
+ self._append_count = 0
56
+ logger.info(f"HelloWorldBridgePlugin initialized: {self.bridge_id}")
57
+
58
+ async def on_activate(self) -> None:
59
+ """
60
+ Called when the bridge is activated.
61
+
62
+ This is where you'd initialize connections, load resources, etc.
63
+ """
64
+ self._active = True
65
+ self._append_count = 0
66
+ logger.info(f"HelloWorldBridge activated: {self.bridge_id}")
67
+
68
+ async def on_deactivate(self) -> None:
69
+ """
70
+ Called when the bridge is deactivated.
71
+
72
+ This is where you'd clean up connections, save state, etc.
73
+ """
74
+ self._active = False
75
+ logger.info(
76
+ f"HelloWorldBridge deactivated: {self.bridge_id} "
77
+ f"(processed {self._append_count} appends)"
78
+ )
79
+
80
+ async def health_check(self) -> Dict[str, Any]:
81
+ """
82
+ Return health status for monitoring.
83
+
84
+ The BridgeRegistry calls this periodically to monitor bridge health.
85
+
86
+ Returns:
87
+ Dict with 'healthy' bool and optional details:
88
+ {
89
+ "healthy": True,
90
+ "bridge_id": "hello_world",
91
+ "append_count": 42,
92
+ "message": "Optional status message"
93
+ }
94
+ """
95
+ return {
96
+ "healthy": self._active,
97
+ "bridge_id": self.bridge_id,
98
+ "append_count": self._append_count,
99
+ "message": "Hello from the example bridge!"
100
+ }
101
+
102
+ async def pre_append(self, entry_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
103
+ """
104
+ Called before an entry is appended.
105
+
106
+ This hook can:
107
+ - Modify entry_data and return the modified version
108
+ - Return None to use the original entry_data unchanged
109
+ - Return False to reject the append (only if critical=true in manifest)
110
+
111
+ Example use cases:
112
+ - Add metadata tags
113
+ - Enrich entries with external data
114
+ - Validate entry format
115
+ - Filter sensitive information
116
+
117
+ Args:
118
+ entry_data: The entry about to be appended
119
+
120
+ Returns:
121
+ Modified entry_data dict, None for original, or False to reject
122
+ """
123
+ # Example: Add a tag to indicate this went through our bridge
124
+ if entry_data.get("meta") is None:
125
+ entry_data["meta"] = {}
126
+
127
+ entry_data["meta"]["hello_world_processed"] = True
128
+
129
+ logger.debug(
130
+ f"HelloWorldBridge pre_append: "
131
+ f"{entry_data.get('message', '')[:50]}"
132
+ )
133
+
134
+ return entry_data
135
+
136
+ async def post_append(
137
+ self,
138
+ entry_data: Dict[str, Any],
139
+ result: Dict[str, Any]
140
+ ) -> None:
141
+ """
142
+ Called after an entry is successfully appended.
143
+
144
+ This hook is for side effects only - you cannot modify the entry.
145
+
146
+ Example use cases:
147
+ - Send notifications
148
+ - Update metrics/dashboards
149
+ - Trigger downstream workflows
150
+ - Log to external systems
151
+
152
+ Args:
153
+ entry_data: The entry that was appended
154
+ result: The result from the append operation
155
+ """
156
+ self._append_count += 1
157
+
158
+ logger.debug(
159
+ f"HelloWorldBridge post_append #{self._append_count}: "
160
+ f"success={result.get('ok')}"
161
+ )
162
+
163
+
164
+ def create_plugin(manifest: Any) -> HelloWorldBridgePlugin:
165
+ """
166
+ Factory function to create plugin instance.
167
+
168
+ The BridgeRegistry uses this function to instantiate the plugin.
169
+ This pattern allows for plugin initialization customization.
170
+
171
+ Args:
172
+ manifest: Loaded BridgeManifest configuration
173
+
174
+ Returns:
175
+ Initialized HelloWorldBridgePlugin instance
176
+ """
177
+ return HelloWorldBridgePlugin(manifest)