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.
- scribe_mcp/__init__.py +11 -0
- scribe_mcp/__main__.py +31 -0
- scribe_mcp/bridges/__init__.py +46 -0
- scribe_mcp/bridges/api.py +204 -0
- scribe_mcp/bridges/examples/__init__.py +10 -0
- scribe_mcp/bridges/examples/hello_world_plugin.py +177 -0
- scribe_mcp/bridges/health.py +338 -0
- scribe_mcp/bridges/hooks.py +210 -0
- scribe_mcp/bridges/manifest.py +278 -0
- scribe_mcp/bridges/plugin.py +208 -0
- scribe_mcp/bridges/policy.py +115 -0
- scribe_mcp/bridges/registry.py +355 -0
- scribe_mcp/bridges/security.py +101 -0
- scribe_mcp/bridges/tools.py +196 -0
- scribe_mcp/cli/__init__.py +4 -0
- scribe_mcp/cli/main.py +358 -0
- scribe_mcp/cli/session_store.py +113 -0
- scribe_mcp/config/__init__.py +5 -0
- scribe_mcp/config/boundary_rules_schema.json +71 -0
- scribe_mcp/config/display_config.py +254 -0
- scribe_mcp/config/global_log_config.json +57 -0
- scribe_mcp/config/log_config.json +32 -0
- scribe_mcp/config/log_config.py +124 -0
- scribe_mcp/config/logging.py +48 -0
- scribe_mcp/config/mcp_config.json +12 -0
- scribe_mcp/config/paths.py +143 -0
- scribe_mcp/config/reminder_config.json +79 -0
- scribe_mcp/config/reminder_rules.json +196 -0
- scribe_mcp/config/repo_config.py +381 -0
- scribe_mcp/config/scribe_config_template.yaml +65 -0
- scribe_mcp/config/settings.py +236 -0
- scribe_mcp/config/vector_config.py +208 -0
- scribe_mcp/db/__init__.py +4 -0
- scribe_mcp/db/init.sql +210 -0
- scribe_mcp/doc_management/__init__.py +1 -0
- scribe_mcp/doc_management/actions/__init__.py +19 -0
- scribe_mcp/doc_management/actions/append.py +14 -0
- scribe_mcp/doc_management/actions/batch.py +73 -0
- scribe_mcp/doc_management/actions/create.py +80 -0
- scribe_mcp/doc_management/actions/edit.py +336 -0
- scribe_mcp/doc_management/actions/query.py +332 -0
- scribe_mcp/doc_management/actions/search.py +208 -0
- scribe_mcp/doc_management/actions/status.py +14 -0
- scribe_mcp/doc_management/change_logger.py +508 -0
- scribe_mcp/doc_management/change_rollback.py +639 -0
- scribe_mcp/doc_management/cli.py +208 -0
- scribe_mcp/doc_management/conflict_resolver.py +463 -0
- scribe_mcp/doc_management/diff_visualizer.py +492 -0
- scribe_mcp/doc_management/file_watcher.py +384 -0
- scribe_mcp/doc_management/healing.py +252 -0
- scribe_mcp/doc_management/indexing.py +295 -0
- scribe_mcp/doc_management/integrity_verifier.py +508 -0
- scribe_mcp/doc_management/manager.py +2980 -0
- scribe_mcp/doc_management/performance_monitor.py +554 -0
- scribe_mcp/doc_management/preflight.py +86 -0
- scribe_mcp/doc_management/runtime.py +576 -0
- scribe_mcp/doc_management/special_create.py +583 -0
- scribe_mcp/doc_management/special_indexes.py +430 -0
- scribe_mcp/doc_management/sync_manager.py +523 -0
- scribe_mcp/doc_management/utils.py +198 -0
- scribe_mcp/doc_management/validation.py +52 -0
- scribe_mcp/plugins/__init__.py +2 -0
- scribe_mcp/plugins/registry.py +541 -0
- scribe_mcp/plugins/vector_indexer.json +53 -0
- scribe_mcp/plugins/vector_indexer.py +996 -0
- scribe_mcp/py.typed +1 -0
- scribe_mcp/reminders.py +418 -0
- scribe_mcp/scripts/__init__.py +0 -0
- scribe_mcp/scripts/backfill_log_type.py +118 -0
- scribe_mcp/scripts/check_vector_index.py +104 -0
- scribe_mcp/scripts/migrate_database.py +314 -0
- scribe_mcp/scripts/reindex_docs.py +49 -0
- scribe_mcp/scripts/reindex_vector.py +586 -0
- scribe_mcp/scripts/scribe.py +362 -0
- scribe_mcp/scripts/scribe_admin.py +571 -0
- scribe_mcp/scripts/scribe_cli.py +431 -0
- scribe_mcp/scripts/scribe_probe.py +404 -0
- scribe_mcp/scripts/test_mcp_server.py +168 -0
- scribe_mcp/scripts/verify_migration.py +117 -0
- scribe_mcp/security/__init__.py +2 -0
- scribe_mcp/security/sandbox.py +364 -0
- scribe_mcp/server.py +908 -0
- scribe_mcp/shared/__init__.py +3 -0
- scribe_mcp/shared/base_logging_tool.py +139 -0
- scribe_mcp/shared/execution_context.py +244 -0
- scribe_mcp/shared/log_enums.py +193 -0
- scribe_mcp/shared/logging_utils.py +732 -0
- scribe_mcp/shared/project_registry.py +716 -0
- scribe_mcp/shared/project_utils.py +153 -0
- scribe_mcp/shared/session_utils.py +80 -0
- scribe_mcp/shared/tool_runtime.py +395 -0
- scribe_mcp/state/__init__.py +6 -0
- scribe_mcp/state/agent_identity.py +260 -0
- scribe_mcp/state/agent_manager.py +526 -0
- scribe_mcp/state/manager.py +407 -0
- scribe_mcp/storage/__init__.py +30 -0
- scribe_mcp/storage/base.py +400 -0
- scribe_mcp/storage/models.py +210 -0
- scribe_mcp/storage/pool.py +409 -0
- scribe_mcp/storage/postgres.py +260 -0
- scribe_mcp/storage/sqlite/__init__.py +433 -0
- scribe_mcp/storage/sqlite/compat_migrations.py +200 -0
- scribe_mcp/storage/sqlite/documents.py +60 -0
- scribe_mcp/storage/sqlite/domain_facade.py +547 -0
- scribe_mcp/storage/sqlite/entries.py +451 -0
- scribe_mcp/storage/sqlite/internals.py +259 -0
- scribe_mcp/storage/sqlite/migrations.py +308 -0
- scribe_mcp/storage/sqlite/planning.py +308 -0
- scribe_mcp/storage/sqlite/projects.py +251 -0
- scribe_mcp/storage/sqlite/schema.py +491 -0
- scribe_mcp/storage/sqlite/sessions.py +449 -0
- scribe_mcp/storage/sqlite/telemetry.py +398 -0
- scribe_mcp/storage/sqlite/telemetry_support.py +275 -0
- scribe_mcp/template_engine/__init__.py +11 -0
- scribe_mcp/template_engine/cli.py +160 -0
- scribe_mcp/template_engine/engine.py +593 -0
- scribe_mcp/templates/__init__.py +194 -0
- scribe_mcp/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- scribe_mcp/templates/custom/project_header.md +51 -0
- scribe_mcp/templates/documents/AGENT_REPORT_CARD_TEMPLATE.md +158 -0
- scribe_mcp/templates/documents/ARCHITECTURE_GUIDE_TEMPLATE.md +124 -0
- scribe_mcp/templates/documents/BUG_LOG_TEMPLATE.md +76 -0
- scribe_mcp/templates/documents/BUG_REPORT_TEMPLATE.md +78 -0
- scribe_mcp/templates/documents/CHECKLIST_TEMPLATE.md +59 -0
- scribe_mcp/templates/documents/DOC_LOG_TEMPLATE.md +48 -0
- scribe_mcp/templates/documents/GLOBAL_PROGRESS_LOG_TEMPLATE.md +87 -0
- scribe_mcp/templates/documents/PHASE_PLAN_TEMPLATE.md +97 -0
- scribe_mcp/templates/documents/PROGRESS_LOG_TEMPLATE.md +38 -0
- scribe_mcp/templates/documents/RESEARCH_REPORT_TEMPLATE.md +66 -0
- scribe_mcp/templates/documents/REVIEW_REPORT_TEMPLATE.md +135 -0
- scribe_mcp/templates/documents/SECURITY_LOG_TEMPLATE.md +60 -0
- scribe_mcp/templates/documents/base_document.md +85 -0
- scribe_mcp/templates/documents/base_log.md +61 -0
- scribe_mcp/templates/fragments/api_endpoints.md +26 -0
- scribe_mcp/templates/fragments/directory_structure.md +43 -0
- scribe_mcp/templates/fragments/testing_strategy.md +50 -0
- scribe_mcp/tools/__init__.py +39 -0
- scribe_mcp/tools/agent_project_utils.py +192 -0
- scribe_mcp/tools/append_entry.py +2189 -0
- scribe_mcp/tools/base/__init__.py +34 -0
- scribe_mcp/tools/base/base_tool.py +150 -0
- scribe_mcp/tools/base/parameter_normalizer.py +163 -0
- scribe_mcp/tools/base/tool_metadata.py +482 -0
- scribe_mcp/tools/base/tool_result.py +89 -0
- scribe_mcp/tools/config/append_entry_config.py +602 -0
- scribe_mcp/tools/config/query_entries_config.py +590 -0
- scribe_mcp/tools/config/rotate_log_config.py +494 -0
- scribe_mcp/tools/constants.py +11 -0
- scribe_mcp/tools/delete_project.py +238 -0
- scribe_mcp/tools/doctor.py +113 -0
- scribe_mcp/tools/edit_file.py +388 -0
- scribe_mcp/tools/generate_doc_templates.py +597 -0
- scribe_mcp/tools/get_project.py +649 -0
- scribe_mcp/tools/health_check.py +315 -0
- scribe_mcp/tools/list_projects.py +734 -0
- scribe_mcp/tools/manage_docs.py +244 -0
- scribe_mcp/tools/manage_docs_validation.py +287 -0
- scribe_mcp/tools/project_utils.py +254 -0
- scribe_mcp/tools/query_entries.py +2204 -0
- scribe_mcp/tools/read_file.py +2553 -0
- scribe_mcp/tools/read_recent.py +591 -0
- scribe_mcp/tools/rotate_log.py +2132 -0
- scribe_mcp/tools/search.py +919 -0
- scribe_mcp/tools/sentinel_tools.py +710 -0
- scribe_mcp/tools/set_project.py +963 -0
- scribe_mcp/tools/vector_search.py +419 -0
- scribe_mcp/utils/__init__.py +26 -0
- scribe_mcp/utils/audit.py +403 -0
- scribe_mcp/utils/bulk_processor.py +585 -0
- scribe_mcp/utils/config_manager.py +1504 -0
- scribe_mcp/utils/context_safety.py +293 -0
- scribe_mcp/utils/diff_compiler.py +24 -0
- scribe_mcp/utils/entry_limit.py +151 -0
- scribe_mcp/utils/error_handler.py +1517 -0
- scribe_mcp/utils/estimator.py +768 -0
- scribe_mcp/utils/files.py +864 -0
- scribe_mcp/utils/formatters/__init__.py +37 -0
- scribe_mcp/utils/formatters/base.py +323 -0
- scribe_mcp/utils/formatters/dispatcher.py +470 -0
- scribe_mcp/utils/formatters/entry.py +743 -0
- scribe_mcp/utils/formatters/file.py +767 -0
- scribe_mcp/utils/formatters/project.py +1022 -0
- scribe_mcp/utils/formatters/ui.py +427 -0
- scribe_mcp/utils/frontmatter.py +125 -0
- scribe_mcp/utils/integrity.py +264 -0
- scribe_mcp/utils/logs.py +94 -0
- scribe_mcp/utils/parameter_validator.py +1538 -0
- scribe_mcp/utils/path_suggestions.py +249 -0
- scribe_mcp/utils/path_utils.py +107 -0
- scribe_mcp/utils/reminder_engine.py +606 -0
- scribe_mcp/utils/reminder_monitoring.py +523 -0
- scribe_mcp/utils/reminder_validator.py +285 -0
- scribe_mcp/utils/response.py +1365 -0
- scribe_mcp/utils/rotation_state.py +532 -0
- scribe_mcp/utils/search.py +40 -0
- scribe_mcp/utils/sentinel_logs.py +182 -0
- scribe_mcp/utils/slug.py +88 -0
- scribe_mcp/utils/time.py +80 -0
- scribe_mcp/utils/tokens.py +360 -0
- scribe_mcp/utils/tool_logger.py +208 -0
- scribe_mcp-2.2.dist-info/METADATA +998 -0
- scribe_mcp-2.2.dist-info/RECORD +206 -0
- scribe_mcp-2.2.dist-info/WHEEL +5 -0
- scribe_mcp-2.2.dist-info/entry_points.txt +4 -0
- scribe_mcp-2.2.dist-info/licenses/LICENSE +46 -0
- scribe_mcp-2.2.dist-info/top_level.txt +1 -0
scribe_mcp/__init__.py
ADDED
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)
|