memory-journal-mcp 4.4.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/codeql.yml +1 -6
- package/.github/workflows/docker-publish.yml +15 -49
- package/.github/workflows/lint-and-test.yml +1 -1
- package/.github/workflows/secrets-scanning.yml +4 -3
- package/.github/workflows/security-update.yml +3 -3
- package/CHANGELOG.md +213 -0
- package/CONTRIBUTING.md +132 -97
- package/DOCKER_README.md +184 -235
- package/Dockerfile +27 -24
- package/README.md +218 -190
- package/SECURITY.md +27 -35
- package/dist/cli.js +16 -1
- package/dist/cli.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +5 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +133 -73
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/constants/icons.d.ts +2 -2
- package/dist/constants/icons.d.ts.map +1 -1
- package/dist/constants/icons.js +7 -6
- package/dist/constants/icons.js.map +1 -1
- package/dist/database/SqliteAdapter.d.ts +37 -24
- package/dist/database/SqliteAdapter.d.ts.map +1 -1
- package/dist/database/SqliteAdapter.js +319 -157
- package/dist/database/SqliteAdapter.js.map +1 -1
- package/dist/database/schema.d.ts +45 -0
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/database/schema.js +92 -0
- package/dist/database/schema.js.map +1 -0
- package/dist/filtering/ToolFilter.d.ts +1 -1
- package/dist/filtering/ToolFilter.d.ts.map +1 -1
- package/dist/filtering/ToolFilter.js +13 -2
- package/dist/filtering/ToolFilter.js.map +1 -1
- package/dist/github/GitHubIntegration.d.ts.map +1 -1
- package/dist/github/GitHubIntegration.js +1 -3
- package/dist/github/GitHubIntegration.js.map +1 -1
- package/dist/handlers/prompts/github.d.ts +12 -0
- package/dist/handlers/prompts/github.d.ts.map +1 -0
- package/dist/handlers/prompts/github.js +178 -0
- package/dist/handlers/prompts/github.js.map +1 -0
- package/dist/handlers/prompts/index.d.ts +23 -2
- package/dist/handlers/prompts/index.d.ts.map +1 -1
- package/dist/handlers/prompts/index.js +7 -432
- package/dist/handlers/prompts/index.js.map +1 -1
- package/dist/handlers/prompts/workflow.d.ts +12 -0
- package/dist/handlers/prompts/workflow.d.ts.map +1 -0
- package/dist/handlers/prompts/workflow.js +277 -0
- package/dist/handlers/prompts/workflow.js.map +1 -0
- package/dist/handlers/resources/core.d.ts +11 -0
- package/dist/handlers/resources/core.d.ts.map +1 -0
- package/dist/handlers/resources/core.js +433 -0
- package/dist/handlers/resources/core.js.map +1 -0
- package/dist/handlers/resources/github.d.ts +11 -0
- package/dist/handlers/resources/github.d.ts.map +1 -0
- package/dist/handlers/resources/github.js +314 -0
- package/dist/handlers/resources/github.js.map +1 -0
- package/dist/handlers/resources/graph.d.ts +11 -0
- package/dist/handlers/resources/graph.d.ts.map +1 -0
- package/dist/handlers/resources/graph.js +204 -0
- package/dist/handlers/resources/graph.js.map +1 -0
- package/dist/handlers/resources/index.d.ts +5 -20
- package/dist/handlers/resources/index.d.ts.map +1 -1
- package/dist/handlers/resources/index.js +16 -1278
- package/dist/handlers/resources/index.js.map +1 -1
- package/dist/handlers/resources/shared.d.ts +60 -0
- package/dist/handlers/resources/shared.d.ts.map +1 -0
- package/dist/handlers/resources/shared.js +49 -0
- package/dist/handlers/resources/shared.js.map +1 -0
- package/dist/handlers/resources/team.d.ts +13 -0
- package/dist/handlers/resources/team.d.ts.map +1 -0
- package/dist/handlers/resources/team.js +119 -0
- package/dist/handlers/resources/team.js.map +1 -0
- package/dist/handlers/resources/templates.d.ts +13 -0
- package/dist/handlers/resources/templates.d.ts.map +1 -0
- package/dist/handlers/resources/templates.js +310 -0
- package/dist/handlers/resources/templates.js.map +1 -0
- package/dist/handlers/tools/admin.d.ts +8 -0
- package/dist/handlers/tools/admin.d.ts.map +1 -0
- package/dist/handlers/tools/admin.js +270 -0
- package/dist/handlers/tools/admin.js.map +1 -0
- package/dist/handlers/tools/analytics.d.ts +8 -0
- package/dist/handlers/tools/analytics.d.ts.map +1 -0
- package/dist/handlers/tools/analytics.js +256 -0
- package/dist/handlers/tools/analytics.js.map +1 -0
- package/dist/handlers/tools/backup.d.ts +8 -0
- package/dist/handlers/tools/backup.d.ts.map +1 -0
- package/dist/handlers/tools/backup.js +224 -0
- package/dist/handlers/tools/backup.js.map +1 -0
- package/dist/handlers/tools/core.d.ts +9 -0
- package/dist/handlers/tools/core.d.ts.map +1 -0
- package/dist/handlers/tools/core.js +326 -0
- package/dist/handlers/tools/core.js.map +1 -0
- package/dist/handlers/tools/export.d.ts +8 -0
- package/dist/handlers/tools/export.d.ts.map +1 -0
- package/dist/handlers/tools/export.js +89 -0
- package/dist/handlers/tools/export.js.map +1 -0
- package/dist/handlers/tools/github/helpers.d.ts +34 -0
- package/dist/handlers/tools/github/helpers.d.ts.map +1 -0
- package/dist/handlers/tools/github/helpers.js +52 -0
- package/dist/handlers/tools/github/helpers.js.map +1 -0
- package/dist/handlers/tools/github/insights-tools.d.ts +8 -0
- package/dist/handlers/tools/github/insights-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/insights-tools.js +104 -0
- package/dist/handlers/tools/github/insights-tools.js.map +1 -0
- package/dist/handlers/tools/github/issue-tools.d.ts +8 -0
- package/dist/handlers/tools/github/issue-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/issue-tools.js +359 -0
- package/dist/handlers/tools/github/issue-tools.js.map +1 -0
- package/dist/handlers/tools/github/kanban-tools.d.ts +8 -0
- package/dist/handlers/tools/github/kanban-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/kanban-tools.js +108 -0
- package/dist/handlers/tools/github/kanban-tools.js.map +1 -0
- package/dist/handlers/tools/github/milestone-tools.d.ts +9 -0
- package/dist/handlers/tools/github/milestone-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/milestone-tools.js +302 -0
- package/dist/handlers/tools/github/milestone-tools.js.map +1 -0
- package/dist/handlers/tools/github/mutation-tools.d.ts +12 -0
- package/dist/handlers/tools/github/mutation-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/mutation-tools.js +15 -0
- package/dist/handlers/tools/github/mutation-tools.js.map +1 -0
- package/dist/handlers/tools/github/read-tools.d.ts +8 -0
- package/dist/handlers/tools/github/read-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/read-tools.js +260 -0
- package/dist/handlers/tools/github/read-tools.js.map +1 -0
- package/dist/handlers/tools/github/schemas.d.ts +467 -0
- package/dist/handlers/tools/github/schemas.d.ts.map +1 -0
- package/dist/handlers/tools/github/schemas.js +335 -0
- package/dist/handlers/tools/github/schemas.js.map +1 -0
- package/dist/handlers/tools/github.d.ts +14 -0
- package/dist/handlers/tools/github.d.ts.map +1 -0
- package/dist/handlers/tools/github.js +28 -0
- package/dist/handlers/tools/github.js.map +1 -0
- package/dist/handlers/tools/index.d.ts +15 -20
- package/dist/handlers/tools/index.d.ts.map +1 -1
- package/dist/handlers/tools/index.js +117 -2909
- package/dist/handlers/tools/index.js.map +1 -1
- package/dist/handlers/tools/relationships.d.ts +8 -0
- package/dist/handlers/tools/relationships.d.ts.map +1 -0
- package/dist/handlers/tools/relationships.js +308 -0
- package/dist/handlers/tools/relationships.js.map +1 -0
- package/dist/handlers/tools/schemas.d.ts +108 -0
- package/dist/handlers/tools/schemas.d.ts.map +1 -0
- package/dist/handlers/tools/schemas.js +122 -0
- package/dist/handlers/tools/schemas.js.map +1 -0
- package/dist/handlers/tools/search.d.ts +8 -0
- package/dist/handlers/tools/search.d.ts.map +1 -0
- package/dist/handlers/tools/search.js +282 -0
- package/dist/handlers/tools/search.js.map +1 -0
- package/dist/handlers/tools/team.d.ts +11 -0
- package/dist/handlers/tools/team.d.ts.map +1 -0
- package/dist/handlers/tools/team.js +239 -0
- package/dist/handlers/tools/team.js.map +1 -0
- package/dist/server/McpServer.d.ts +4 -0
- package/dist/server/McpServer.d.ts.map +1 -1
- package/dist/server/McpServer.js +48 -297
- package/dist/server/McpServer.js.map +1 -1
- package/dist/server/Scheduler.d.ts +91 -0
- package/dist/server/Scheduler.d.ts.map +1 -0
- package/dist/server/Scheduler.js +201 -0
- package/dist/server/Scheduler.js.map +1 -0
- package/dist/transports/http.d.ts +66 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +519 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/types/entities.d.ts +101 -0
- package/dist/types/entities.d.ts.map +1 -0
- package/dist/types/entities.js +5 -0
- package/dist/types/entities.js.map +1 -0
- package/dist/types/filtering.d.ts +34 -0
- package/dist/types/filtering.d.ts.map +1 -0
- package/dist/types/filtering.js +5 -0
- package/dist/types/filtering.js.map +1 -0
- package/dist/types/github.d.ts +166 -0
- package/dist/types/github.d.ts.map +1 -0
- package/dist/types/github.js +5 -0
- package/dist/types/github.js.map +1 -0
- package/dist/types/index.d.ts +35 -292
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error-helpers.d.ts +37 -0
- package/dist/utils/error-helpers.d.ts.map +1 -0
- package/dist/utils/error-helpers.js +47 -0
- package/dist/utils/error-helpers.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +6 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/security-utils.d.ts +0 -21
- package/dist/utils/security-utils.d.ts.map +1 -1
- package/dist/utils/security-utils.js +0 -47
- package/dist/utils/security-utils.js.map +1 -1
- package/dist/vector/VectorSearchManager.d.ts.map +1 -1
- package/dist/vector/VectorSearchManager.js +9 -32
- package/dist/vector/VectorSearchManager.js.map +1 -1
- package/docker-compose.yml +11 -2
- package/hooks/README.md +107 -0
- package/hooks/cursor/hooks.json +10 -0
- package/hooks/cursor/memory-journal.mdc +22 -0
- package/hooks/cursor/session-end.sh +19 -0
- package/hooks/kilo-code/session-end-mode.json +11 -0
- package/hooks/kiro/session-end.md +13 -0
- package/mcp-config-example.json +1 -0
- package/package.json +11 -9
- package/playwright.config.ts +29 -0
- package/releases/v4.5.0.md +116 -0
- package/releases/v5.0.0.md +105 -0
- package/scripts/generate-server-instructions.ts +176 -0
- package/scripts/server-instructions-function-body.ts +77 -0
- package/server.json +3 -3
- package/src/cli.ts +45 -1
- package/src/constants/ServerInstructions.ts +133 -73
- package/src/constants/icons.ts +8 -7
- package/src/constants/server-instructions.md +268 -0
- package/src/database/SqliteAdapter.ts +358 -192
- package/src/database/schema.ts +125 -0
- package/src/filtering/ToolFilter.ts +13 -2
- package/src/github/GitHubIntegration.ts +1 -3
- package/src/handlers/prompts/github.ts +209 -0
- package/src/handlers/prompts/index.ts +10 -499
- package/src/handlers/prompts/workflow.ts +314 -0
- package/src/handlers/resources/core.ts +528 -0
- package/src/handlers/resources/github.ts +358 -0
- package/src/handlers/resources/graph.ts +254 -0
- package/src/handlers/resources/index.ts +23 -1570
- package/src/handlers/resources/shared.ts +103 -0
- package/src/handlers/resources/team.ts +133 -0
- package/src/handlers/resources/templates.ts +374 -0
- package/src/handlers/tools/admin.ts +285 -0
- package/src/handlers/tools/analytics.ts +301 -0
- package/src/handlers/tools/backup.ts +242 -0
- package/src/handlers/tools/core.ts +350 -0
- package/src/handlers/tools/export.ts +115 -0
- package/src/handlers/tools/github/helpers.ts +86 -0
- package/src/handlers/tools/github/insights-tools.ts +119 -0
- package/src/handlers/tools/github/issue-tools.ts +439 -0
- package/src/handlers/tools/github/kanban-tools.ts +134 -0
- package/src/handlers/tools/github/milestone-tools.ts +392 -0
- package/src/handlers/tools/github/mutation-tools.ts +17 -0
- package/src/handlers/tools/github/read-tools.ts +328 -0
- package/src/handlers/tools/github/schemas.ts +369 -0
- package/src/handlers/tools/github.ts +36 -0
- package/src/handlers/tools/index.ts +144 -3325
- package/src/handlers/tools/relationships.ts +358 -0
- package/src/handlers/tools/schemas.ts +132 -0
- package/src/handlers/tools/search.ts +343 -0
- package/src/handlers/tools/team.ts +273 -0
- package/src/server/McpServer.ts +63 -358
- package/src/server/Scheduler.ts +278 -0
- package/src/transports/http.ts +635 -0
- package/src/types/entities.ts +145 -0
- package/src/types/filtering.ts +54 -0
- package/src/types/github.ts +180 -0
- package/src/types/index.ts +67 -375
- package/src/utils/error-helpers.ts +52 -0
- package/src/utils/logger.ts +6 -3
- package/src/utils/security-utils.ts +0 -52
- package/src/vector/VectorSearchManager.ts +9 -33
- package/tests/constants/icons.test.ts +1 -2
- package/tests/constants/server-instructions.test.ts +30 -4
- package/tests/database/sqlite-adapter.test.ts +91 -7
- package/tests/e2e/auth.spec.ts +154 -0
- package/tests/e2e/health.spec.ts +63 -0
- package/tests/e2e/protocols.spec.ts +134 -0
- package/tests/e2e/resources.spec.ts +103 -0
- package/tests/e2e/scheduler.spec.ts +79 -0
- package/tests/e2e/security.spec.ts +91 -0
- package/tests/e2e/sessions.spec.ts +95 -0
- package/tests/e2e/stateless.spec.ts +121 -0
- package/tests/e2e/tools.spec.ts +111 -0
- package/tests/filtering/tool-filter.test.ts +46 -0
- package/tests/handlers/error-path-coverage.test.ts +324 -0
- package/tests/handlers/github-resource-handlers.test.ts +453 -0
- package/tests/handlers/github-tool-handlers.test.ts +899 -0
- package/tests/handlers/prompt-handler-coverage.test.ts +106 -0
- package/tests/handlers/prompt-handlers.test.ts +40 -0
- package/tests/handlers/resource-handler-coverage.test.ts +181 -0
- package/tests/handlers/resource-handlers.test.ts +33 -9
- package/tests/handlers/search-tool-handlers.test.ts +272 -0
- package/tests/handlers/targeted-gap-closure.test.ts +387 -0
- package/tests/handlers/team-resource-handlers.test.ts +156 -0
- package/tests/handlers/team-tool-handlers.test.ts +301 -0
- package/tests/handlers/tool-handler-coverage.test.ts +469 -0
- package/tests/handlers/tool-handlers.test.ts +2 -2
- package/tests/security/sql-injection.test.ts +3 -54
- package/tests/server/mcp-server.test.ts +503 -8
- package/tests/server/scheduler.test.ts +400 -0
- package/tests/transports/http-transport.test.ts +620 -0
- package/tests/vector/vector-search-manager.test.ts +60 -0
- package/vitest.config.ts +4 -1
- package/.memory-journal-team.db +0 -0
- package/.vscode/settings.json +0 -84
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Journal MCP Server - Resource Shared Types & Helpers
|
|
3
|
+
*
|
|
4
|
+
* Shared types, helpers, and utilities used by all resource group modules.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { SqliteAdapter } from '../../database/SqliteAdapter.js'
|
|
8
|
+
import type { VectorSearchManager } from '../../vector/VectorSearchManager.js'
|
|
9
|
+
import type { ToolFilterConfig } from '../../filtering/ToolFilter.js'
|
|
10
|
+
import type { McpIcon } from '../../types/index.js'
|
|
11
|
+
import type { GitHubIntegration } from '../../github/GitHubIntegration.js'
|
|
12
|
+
import type { Scheduler } from '../../server/Scheduler.js'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Resource context for handlers that need extended access
|
|
16
|
+
*/
|
|
17
|
+
export interface ResourceContext {
|
|
18
|
+
db: SqliteAdapter
|
|
19
|
+
teamDb?: SqliteAdapter
|
|
20
|
+
vectorManager?: VectorSearchManager
|
|
21
|
+
filterConfig?: ToolFilterConfig | null
|
|
22
|
+
github?: GitHubIntegration | null
|
|
23
|
+
scheduler?: Scheduler | null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resource handler result with optional annotations for MCP 2025-11-25
|
|
28
|
+
*/
|
|
29
|
+
export interface ResourceResult {
|
|
30
|
+
data: unknown
|
|
31
|
+
annotations?: {
|
|
32
|
+
lastModified?: string // ISO 8601 timestamp
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Internal resource definition with db handler
|
|
38
|
+
*/
|
|
39
|
+
export interface InternalResourceDef {
|
|
40
|
+
uri: string
|
|
41
|
+
name: string
|
|
42
|
+
title: string
|
|
43
|
+
description: string
|
|
44
|
+
mimeType: string
|
|
45
|
+
icons?: McpIcon[] // MCP 2025-11-25 icons
|
|
46
|
+
annotations?: {
|
|
47
|
+
audience?: ('user' | 'assistant')[]
|
|
48
|
+
priority?: number
|
|
49
|
+
lastModified?: string
|
|
50
|
+
autoRead?: boolean
|
|
51
|
+
sessionInit?: boolean
|
|
52
|
+
}
|
|
53
|
+
handler: (uri: string, context: ResourceContext) => unknown
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Execute a raw SQL query on the database
|
|
58
|
+
*/
|
|
59
|
+
export function execQuery(
|
|
60
|
+
db: SqliteAdapter,
|
|
61
|
+
sql: string,
|
|
62
|
+
params: unknown[] = []
|
|
63
|
+
): Record<string, unknown>[] {
|
|
64
|
+
const rawDb = db.getRawDb()
|
|
65
|
+
const result = rawDb.exec(sql, params)
|
|
66
|
+
if (result.length === 0) return []
|
|
67
|
+
|
|
68
|
+
const columns = result[0]?.columns ?? []
|
|
69
|
+
return (result[0]?.values ?? []).map((values: unknown[]) => {
|
|
70
|
+
const obj: Record<string, unknown> = {}
|
|
71
|
+
columns.forEach((col: string, i: number) => {
|
|
72
|
+
obj[col] = values[i]
|
|
73
|
+
})
|
|
74
|
+
return obj
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Transform snake_case SQL row to camelCase entry object
|
|
80
|
+
* Ensures consistency with SqliteAdapter.getRecentEntries() output
|
|
81
|
+
*/
|
|
82
|
+
export function transformEntryRow(row: Record<string, unknown>): Record<string, unknown> {
|
|
83
|
+
return {
|
|
84
|
+
id: row['id'],
|
|
85
|
+
entryType: row['entry_type'],
|
|
86
|
+
content: row['content'],
|
|
87
|
+
timestamp: row['timestamp'],
|
|
88
|
+
isPersonal: row['is_personal'] === 1 || row['is_personal'] === true,
|
|
89
|
+
significanceType: row['significance_type'] ?? null,
|
|
90
|
+
autoContext: row['auto_context'] ?? null,
|
|
91
|
+
deletedAt: row['deleted_at'] ?? null,
|
|
92
|
+
projectNumber: row['project_number'] ?? null,
|
|
93
|
+
projectOwner: row['project_owner'] ?? null,
|
|
94
|
+
issueNumber: row['issue_number'] ?? null,
|
|
95
|
+
issueUrl: row['issue_url'] ?? null,
|
|
96
|
+
prNumber: row['pr_number'] ?? null,
|
|
97
|
+
prUrl: row['pr_url'] ?? null,
|
|
98
|
+
prStatus: row['pr_status'] ?? null,
|
|
99
|
+
workflowRunId: row['workflow_run_id'] ?? null,
|
|
100
|
+
workflowName: row['workflow_name'] ?? null,
|
|
101
|
+
workflowStatus: row['workflow_status'] ?? null,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team Resource Definitions - 2 resources
|
|
3
|
+
*
|
|
4
|
+
* Resources: memory://team/recent, memory://team/statistics
|
|
5
|
+
*
|
|
6
|
+
* Requires TEAM_DB_PATH to be configured.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ICON_CLOCK, ICON_TEAM } from '../../constants/icons.js'
|
|
10
|
+
import type { InternalResourceDef, ResourceContext, ResourceResult } from './shared.js'
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Helpers
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Enrich entries with author column from the team database
|
|
18
|
+
*/
|
|
19
|
+
function enrichWithAuthor<T extends { id: number }>(
|
|
20
|
+
entries: T[],
|
|
21
|
+
context: ResourceContext
|
|
22
|
+
): (T & { author: string | null })[] {
|
|
23
|
+
if (!context.teamDb) return entries.map((e) => ({ ...e, author: null }))
|
|
24
|
+
const rawDb = context.teamDb.getRawDb()
|
|
25
|
+
return entries.map((e) => {
|
|
26
|
+
const authorResult = rawDb.exec('SELECT author FROM memory_journal WHERE id = ?', [e.id])
|
|
27
|
+
const author = (authorResult[0]?.values[0]?.[0] as string) ?? null
|
|
28
|
+
return { ...e, author }
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Resource Definitions
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get team resource definitions
|
|
38
|
+
*/
|
|
39
|
+
export function getTeamResourceDefinitions(): InternalResourceDef[] {
|
|
40
|
+
return [
|
|
41
|
+
{
|
|
42
|
+
uri: 'memory://team/recent',
|
|
43
|
+
name: 'Recent Team Entries',
|
|
44
|
+
title: 'Recent Team-Shared Entries',
|
|
45
|
+
description:
|
|
46
|
+
'Recent entries from the team database. Requires TEAM_DB_PATH configuration.',
|
|
47
|
+
mimeType: 'application/json',
|
|
48
|
+
icons: [ICON_CLOCK],
|
|
49
|
+
annotations: {
|
|
50
|
+
audience: ['assistant'],
|
|
51
|
+
priority: 0.7,
|
|
52
|
+
},
|
|
53
|
+
handler: (_uri: string, context: ResourceContext): ResourceResult => {
|
|
54
|
+
if (!context.teamDb) {
|
|
55
|
+
return {
|
|
56
|
+
data: {
|
|
57
|
+
error: 'Team database not configured. Set TEAM_DB_PATH to enable.',
|
|
58
|
+
entries: [],
|
|
59
|
+
count: 0,
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const entries = context.teamDb.getRecentEntries(10)
|
|
65
|
+
const lastModified = entries[0]?.timestamp ?? new Date().toISOString()
|
|
66
|
+
const enriched = enrichWithAuthor(entries, context)
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
data: {
|
|
70
|
+
entries: enriched,
|
|
71
|
+
count: enriched.length,
|
|
72
|
+
source: 'team',
|
|
73
|
+
},
|
|
74
|
+
annotations: { lastModified },
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
uri: 'memory://team/statistics',
|
|
80
|
+
name: 'Team Statistics',
|
|
81
|
+
title: 'Team Database Statistics',
|
|
82
|
+
description: 'Entry counts, types, and contributor breakdown for the team database.',
|
|
83
|
+
mimeType: 'application/json',
|
|
84
|
+
icons: [ICON_TEAM],
|
|
85
|
+
annotations: {
|
|
86
|
+
audience: ['assistant'],
|
|
87
|
+
priority: 0.6,
|
|
88
|
+
},
|
|
89
|
+
handler: (_uri: string, context: ResourceContext): ResourceResult => {
|
|
90
|
+
if (!context.teamDb) {
|
|
91
|
+
return {
|
|
92
|
+
data: {
|
|
93
|
+
error: 'Team database not configured. Set TEAM_DB_PATH to enable.',
|
|
94
|
+
configured: false,
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const stats = context.teamDb.getStatistics('week')
|
|
100
|
+
const rawDb = context.teamDb.getRawDb()
|
|
101
|
+
|
|
102
|
+
// Author breakdown
|
|
103
|
+
let authors: { author: string; count: number }[] = []
|
|
104
|
+
try {
|
|
105
|
+
const authorResult = rawDb.exec(
|
|
106
|
+
`SELECT COALESCE(author, 'unknown') as author, COUNT(*) as count
|
|
107
|
+
FROM memory_journal
|
|
108
|
+
WHERE deleted_at IS NULL
|
|
109
|
+
GROUP BY COALESCE(author, 'unknown')
|
|
110
|
+
ORDER BY count DESC`
|
|
111
|
+
)
|
|
112
|
+
if (authorResult[0]) {
|
|
113
|
+
authors = authorResult[0].values.map((row) => ({
|
|
114
|
+
author: row[0] as string,
|
|
115
|
+
count: row[1] as number,
|
|
116
|
+
}))
|
|
117
|
+
}
|
|
118
|
+
} catch {
|
|
119
|
+
// Author column may not exist yet
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
data: {
|
|
124
|
+
configured: true,
|
|
125
|
+
...stats,
|
|
126
|
+
authors,
|
|
127
|
+
source: 'team',
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
]
|
|
133
|
+
}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Journal MCP Server - Template Resource Definitions
|
|
3
|
+
*
|
|
4
|
+
* Resources with URI templates (parameterized):
|
|
5
|
+
* projects/{number}/timeline, issues/{issue_number}/entries, prs/{pr_number}/entries,
|
|
6
|
+
* prs/{pr_number}/timeline, kanban/{project_number}, kanban/{project_number}/diagram
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ICON_ISSUE, ICON_PR } from '../../constants/icons.js'
|
|
10
|
+
import type { InternalResourceDef, ResourceContext } from './shared.js'
|
|
11
|
+
import { execQuery, transformEntryRow } from './shared.js'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get template resource definitions
|
|
15
|
+
*/
|
|
16
|
+
export function getTemplateResourceDefinitions(): InternalResourceDef[] {
|
|
17
|
+
return [
|
|
18
|
+
{
|
|
19
|
+
uri: 'memory://projects/{number}/timeline',
|
|
20
|
+
name: 'Project Timeline',
|
|
21
|
+
title: 'Project Activity Timeline',
|
|
22
|
+
description: 'Project activity timeline',
|
|
23
|
+
mimeType: 'application/json',
|
|
24
|
+
annotations: {
|
|
25
|
+
audience: ['assistant'],
|
|
26
|
+
priority: 0.6,
|
|
27
|
+
},
|
|
28
|
+
handler: (uri: string, context: ResourceContext) => {
|
|
29
|
+
const match = /memory:\/\/projects\/(\d+)\/timeline/.exec(uri)
|
|
30
|
+
const projectNumber = match?.[1] ? parseInt(match[1], 10) : null
|
|
31
|
+
|
|
32
|
+
if (projectNumber === null) {
|
|
33
|
+
return { error: 'Invalid project number' }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const rows = execQuery(
|
|
37
|
+
context.db,
|
|
38
|
+
`
|
|
39
|
+
SELECT * FROM memory_journal
|
|
40
|
+
WHERE project_number = ?
|
|
41
|
+
AND deleted_at IS NULL
|
|
42
|
+
ORDER BY timestamp DESC
|
|
43
|
+
LIMIT 50
|
|
44
|
+
`,
|
|
45
|
+
[projectNumber]
|
|
46
|
+
)
|
|
47
|
+
const entries = rows.map(transformEntryRow)
|
|
48
|
+
return { projectNumber, entries, count: entries.length }
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
uri: 'memory://issues/{issue_number}/entries',
|
|
53
|
+
name: 'Issue Entries',
|
|
54
|
+
title: 'Entries Linked to Issue',
|
|
55
|
+
description: 'All entries linked to a specific issue',
|
|
56
|
+
mimeType: 'application/json',
|
|
57
|
+
icons: [ICON_ISSUE],
|
|
58
|
+
annotations: {
|
|
59
|
+
audience: ['assistant'],
|
|
60
|
+
priority: 0.6,
|
|
61
|
+
},
|
|
62
|
+
handler: (uri: string, context: ResourceContext) => {
|
|
63
|
+
const match = /memory:\/\/issues\/(\d+)\/entries/.exec(uri)
|
|
64
|
+
const issueNumber = match?.[1] ? parseInt(match[1], 10) : null
|
|
65
|
+
|
|
66
|
+
if (issueNumber === null) {
|
|
67
|
+
return { error: 'Invalid issue number' }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const rows = execQuery(
|
|
71
|
+
context.db,
|
|
72
|
+
`
|
|
73
|
+
SELECT * FROM memory_journal
|
|
74
|
+
WHERE issue_number = ?
|
|
75
|
+
AND deleted_at IS NULL
|
|
76
|
+
ORDER BY timestamp DESC
|
|
77
|
+
`,
|
|
78
|
+
[issueNumber]
|
|
79
|
+
)
|
|
80
|
+
const entries = rows.map(transformEntryRow)
|
|
81
|
+
return { issueNumber, entries, count: entries.length }
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
uri: 'memory://prs/{pr_number}/entries',
|
|
86
|
+
name: 'PR Entries',
|
|
87
|
+
title: 'Entries Linked to PR',
|
|
88
|
+
description: 'All entries linked to a specific pull request',
|
|
89
|
+
mimeType: 'application/json',
|
|
90
|
+
icons: [ICON_PR],
|
|
91
|
+
annotations: {
|
|
92
|
+
audience: ['assistant'],
|
|
93
|
+
priority: 0.6,
|
|
94
|
+
},
|
|
95
|
+
handler: (uri: string, context: ResourceContext) => {
|
|
96
|
+
const match = /memory:\/\/prs\/(\d+)\/entries/.exec(uri)
|
|
97
|
+
const prNumber = match?.[1] ? parseInt(match[1], 10) : null
|
|
98
|
+
|
|
99
|
+
if (prNumber === null) {
|
|
100
|
+
return { error: 'Invalid PR number' }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const rows = execQuery(
|
|
104
|
+
context.db,
|
|
105
|
+
`
|
|
106
|
+
SELECT * FROM memory_journal
|
|
107
|
+
WHERE pr_number = ?
|
|
108
|
+
AND deleted_at IS NULL
|
|
109
|
+
ORDER BY timestamp DESC
|
|
110
|
+
`,
|
|
111
|
+
[prNumber]
|
|
112
|
+
)
|
|
113
|
+
const entries = rows.map(transformEntryRow)
|
|
114
|
+
return {
|
|
115
|
+
prNumber,
|
|
116
|
+
entries,
|
|
117
|
+
count: entries.length,
|
|
118
|
+
...(entries.length === 0
|
|
119
|
+
? {
|
|
120
|
+
hint: 'No journal entries linked to this PR. Use create_entry with pr_number to link entries.',
|
|
121
|
+
}
|
|
122
|
+
: {}),
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
uri: 'memory://prs/{pr_number}/timeline',
|
|
128
|
+
name: 'PR Timeline',
|
|
129
|
+
title: 'Combined PR and Journal Timeline',
|
|
130
|
+
description: 'Combined PR + journal timeline with live PR metadata',
|
|
131
|
+
mimeType: 'application/json',
|
|
132
|
+
icons: [ICON_PR],
|
|
133
|
+
annotations: {
|
|
134
|
+
audience: ['assistant'],
|
|
135
|
+
priority: 0.5,
|
|
136
|
+
},
|
|
137
|
+
handler: async (uri: string, context: ResourceContext) => {
|
|
138
|
+
const match = /memory:\/\/prs\/(\d+)\/timeline/.exec(uri)
|
|
139
|
+
const prNumber = match?.[1] ? parseInt(match[1], 10) : null
|
|
140
|
+
|
|
141
|
+
if (prNumber === null) {
|
|
142
|
+
return { error: 'Invalid PR number' }
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Fetch live PR metadata from GitHub if available
|
|
146
|
+
let prMetadata: {
|
|
147
|
+
title: string
|
|
148
|
+
state: string
|
|
149
|
+
draft: boolean
|
|
150
|
+
mergedAt: string | null
|
|
151
|
+
closedAt: string | null
|
|
152
|
+
author: string
|
|
153
|
+
headBranch: string
|
|
154
|
+
baseBranch: string
|
|
155
|
+
} | null = null
|
|
156
|
+
|
|
157
|
+
if (context.github) {
|
|
158
|
+
try {
|
|
159
|
+
const repoInfo = await context.github.getRepoInfo()
|
|
160
|
+
if (repoInfo.owner && repoInfo.repo) {
|
|
161
|
+
const pr = await context.github.getPullRequest(
|
|
162
|
+
repoInfo.owner,
|
|
163
|
+
repoInfo.repo,
|
|
164
|
+
prNumber
|
|
165
|
+
)
|
|
166
|
+
if (pr) {
|
|
167
|
+
prMetadata = {
|
|
168
|
+
title: pr.title,
|
|
169
|
+
state: pr.state,
|
|
170
|
+
draft: pr.draft,
|
|
171
|
+
mergedAt: pr.mergedAt,
|
|
172
|
+
closedAt: pr.closedAt,
|
|
173
|
+
author: pr.author,
|
|
174
|
+
headBranch: pr.headBranch,
|
|
175
|
+
baseBranch: pr.baseBranch,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
// GitHub not available, proceed without metadata
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const rows = execQuery(
|
|
185
|
+
context.db,
|
|
186
|
+
`
|
|
187
|
+
SELECT * FROM memory_journal
|
|
188
|
+
WHERE pr_number = ?
|
|
189
|
+
AND deleted_at IS NULL
|
|
190
|
+
ORDER BY timestamp DESC
|
|
191
|
+
`,
|
|
192
|
+
[prNumber]
|
|
193
|
+
)
|
|
194
|
+
const entries = rows.map(transformEntryRow)
|
|
195
|
+
|
|
196
|
+
let timelineNote: string
|
|
197
|
+
if (prMetadata) {
|
|
198
|
+
const stateDesc = prMetadata.state.toLowerCase()
|
|
199
|
+
const mergedNote = prMetadata.mergedAt ? ' (merged)' : ''
|
|
200
|
+
const draftNote = prMetadata.draft ? ' [DRAFT]' : ''
|
|
201
|
+
timelineNote = `PR #${String(prNumber)} is ${stateDesc}${mergedNote}${draftNote}`
|
|
202
|
+
} else {
|
|
203
|
+
timelineNote =
|
|
204
|
+
'GitHub integration unavailable for live PR status. Entry timestamps show journal activity.'
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
prNumber,
|
|
209
|
+
prMetadata,
|
|
210
|
+
entries,
|
|
211
|
+
count: entries.length,
|
|
212
|
+
timelineNote,
|
|
213
|
+
...(entries.length === 0
|
|
214
|
+
? {
|
|
215
|
+
hint: 'No journal entries linked to this PR. Use create_entry with pr_number to link entries.',
|
|
216
|
+
}
|
|
217
|
+
: {}),
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
// Kanban board resources (GitHub Projects v2)
|
|
222
|
+
{
|
|
223
|
+
uri: 'memory://kanban/{project_number}',
|
|
224
|
+
name: 'Kanban Board',
|
|
225
|
+
title: 'GitHub Project Kanban Board',
|
|
226
|
+
description: 'View a GitHub Project v2 as a Kanban board with items grouped by Status',
|
|
227
|
+
mimeType: 'application/json',
|
|
228
|
+
annotations: {
|
|
229
|
+
audience: ['assistant'],
|
|
230
|
+
priority: 0.6,
|
|
231
|
+
},
|
|
232
|
+
handler: async (uri: string, context: ResourceContext) => {
|
|
233
|
+
const match = /memory:\/\/kanban\/(\d+)/.exec(uri)
|
|
234
|
+
const projectNumber = match?.[1] ? parseInt(match[1], 10) : null
|
|
235
|
+
|
|
236
|
+
if (projectNumber === null) {
|
|
237
|
+
return { error: 'Invalid project number' }
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!context.github) {
|
|
241
|
+
return {
|
|
242
|
+
error: 'GitHub integration not available',
|
|
243
|
+
hint: 'Set GITHUB_TOKEN and GITHUB_REPO_PATH environment variables.',
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const repoInfo = await context.github.getRepoInfo()
|
|
248
|
+
const owner = repoInfo.owner
|
|
249
|
+
const repo = repoInfo.repo ?? undefined
|
|
250
|
+
|
|
251
|
+
if (!owner) {
|
|
252
|
+
return {
|
|
253
|
+
error: 'Could not detect repository owner',
|
|
254
|
+
hint: 'Set GITHUB_REPO_PATH to your git repository.',
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const board = await context.github.getProjectKanban(owner, projectNumber, repo)
|
|
259
|
+
if (!board) {
|
|
260
|
+
return {
|
|
261
|
+
error: `Project #${String(projectNumber)} not found or Status field not configured`,
|
|
262
|
+
projectNumber,
|
|
263
|
+
owner,
|
|
264
|
+
hint: 'Projects can be at user, repository, or organization level.',
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return board
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
uri: 'memory://kanban/{project_number}/diagram',
|
|
273
|
+
name: 'Kanban Diagram',
|
|
274
|
+
title: 'Kanban Board Mermaid Diagram',
|
|
275
|
+
description: 'Mermaid diagram visualization of a GitHub Project Kanban board',
|
|
276
|
+
mimeType: 'text/plain',
|
|
277
|
+
annotations: {
|
|
278
|
+
audience: ['user', 'assistant'],
|
|
279
|
+
priority: 0.5,
|
|
280
|
+
},
|
|
281
|
+
handler: async (uri: string, context: ResourceContext) => {
|
|
282
|
+
const match = /memory:\/\/kanban\/(\d+)\/diagram/.exec(uri)
|
|
283
|
+
const projectNumber = match?.[1] ? parseInt(match[1], 10) : null
|
|
284
|
+
|
|
285
|
+
if (projectNumber === null) {
|
|
286
|
+
return { error: 'Invalid project number' }
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (!context.github) {
|
|
290
|
+
return {
|
|
291
|
+
format: 'mermaid',
|
|
292
|
+
diagram: 'graph LR\n NoGitHub[GitHub integration not available]',
|
|
293
|
+
message: 'Set GITHUB_TOKEN and GITHUB_REPO_PATH environment variables.',
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const repoInfo = await context.github.getRepoInfo()
|
|
298
|
+
const owner = repoInfo.owner
|
|
299
|
+
const repo = repoInfo.repo ?? undefined
|
|
300
|
+
|
|
301
|
+
if (!owner) {
|
|
302
|
+
return {
|
|
303
|
+
format: 'mermaid',
|
|
304
|
+
diagram: 'graph LR\n NoOwner[Repository owner not detected]',
|
|
305
|
+
message: 'Set GITHUB_REPO_PATH to your git repository.',
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const board = await context.github.getProjectKanban(owner, projectNumber, repo)
|
|
310
|
+
if (!board) {
|
|
311
|
+
return {
|
|
312
|
+
format: 'mermaid',
|
|
313
|
+
diagram: `graph LR\n NotFound[Project #${String(projectNumber)} not found]`,
|
|
314
|
+
message: 'Ensure the project exists and has a Status field.',
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Build Mermaid diagram with subgraphs for each column
|
|
319
|
+
const lines: string[] = ['graph LR']
|
|
320
|
+
|
|
321
|
+
lines.push(' classDef issue fill:#28a745,color:#fff')
|
|
322
|
+
lines.push(' classDef pr fill:#6f42c1,color:#fff')
|
|
323
|
+
lines.push(' classDef draft fill:#6c757d,color:#fff')
|
|
324
|
+
|
|
325
|
+
for (const column of board.columns) {
|
|
326
|
+
const safeStatus = column.status.replace(/["\s]/g, '_')
|
|
327
|
+
lines.push(
|
|
328
|
+
` subgraph ${safeStatus}["${column.status} (${String(column.items.length)})"]`
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
for (const item of column.items) {
|
|
332
|
+
const safeId = item.id.replace(/[^a-zA-Z0-9]/g, '').slice(-8)
|
|
333
|
+
const label = item.title.slice(0, 25).replace(/["[\]]/g, "'")
|
|
334
|
+
const typeIcon =
|
|
335
|
+
item.type === 'ISSUE'
|
|
336
|
+
? '🔵'
|
|
337
|
+
: item.type === 'PULL_REQUEST'
|
|
338
|
+
? '🟣'
|
|
339
|
+
: '⚪'
|
|
340
|
+
const numberStr =
|
|
341
|
+
item.number !== undefined && item.number !== 0
|
|
342
|
+
? `#${String(item.number)}`
|
|
343
|
+
: ''
|
|
344
|
+
lines.push(` I${safeId}["${typeIcon} ${numberStr} ${label}..."]`)
|
|
345
|
+
|
|
346
|
+
const typeClass =
|
|
347
|
+
item.type === 'ISSUE'
|
|
348
|
+
? 'issue'
|
|
349
|
+
: item.type === 'PULL_REQUEST'
|
|
350
|
+
? 'pr'
|
|
351
|
+
: 'draft'
|
|
352
|
+
lines.push(` class I${safeId} ${typeClass}`)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
lines.push(' end')
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
format: 'mermaid',
|
|
360
|
+
diagram: lines.join('\n'),
|
|
361
|
+
projectNumber,
|
|
362
|
+
projectTitle: board.projectTitle,
|
|
363
|
+
columnCount: board.columns.length,
|
|
364
|
+
totalItems: board.totalItems,
|
|
365
|
+
legend: {
|
|
366
|
+
'🔵': 'Issue',
|
|
367
|
+
'🟣': 'Pull Request',
|
|
368
|
+
'⚪': 'Draft Issue',
|
|
369
|
+
},
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
]
|
|
374
|
+
}
|