memory-journal-mcp 6.1.2 → 6.2.1
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/README.md +44 -28
- package/dist/{chunk-X4SWFATC.js → chunk-BI4ZNSKA.js} +38 -24
- package/dist/{chunk-HCEWINSB.js → chunk-N6EBIDN7.js} +99 -102
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/tools-WPRY5MJ6.js +2 -0
- package/package.json +10 -1
- package/skills/github-commander/SKILL.md +151 -0
- package/skills/github-commander/config/project-config.example.md +125 -0
- package/skills/github-commander/workflows/code-quality-audit.md +80 -0
- package/skills/github-commander/workflows/full-audit.md +134 -0
- package/skills/github-commander/workflows/issue-triage.md +239 -0
- package/skills/github-commander/workflows/milestone-sprint.md +81 -0
- package/skills/github-commander/workflows/perf-audit.md +142 -0
- package/skills/github-commander/workflows/pr-review.md +123 -0
- package/skills/github-commander/workflows/security-audit.md +170 -0
- package/skills/github-commander/workflows/update-deps.md +109 -0
- package/.dockerignore +0 -139
- package/.gitattributes +0 -20
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -95
- package/.github/ISSUE_TEMPLATE/config.yml +0 -11
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -110
- package/.github/ISSUE_TEMPLATE/question.md +0 -78
- package/.github/aw/actions-lock.json +0 -14
- package/.github/copilot-instructions.md +0 -122
- package/.github/dependabot.yml +0 -93
- package/.github/pull_request_template.md +0 -135
- package/.github/workflows/README.md +0 -133
- package/.github/workflows/agentics-maintenance.yml +0 -141
- package/.github/workflows/auto-release.yml +0 -68
- package/.github/workflows/ci-health-monitor.lock.yml +0 -1121
- package/.github/workflows/ci-health-monitor.md +0 -87
- package/.github/workflows/codeql.yml +0 -41
- package/.github/workflows/dependabot-auto-merge.yml +0 -42
- package/.github/workflows/dependency-maintenance.lock.yml +0 -1182
- package/.github/workflows/dependency-maintenance.md +0 -147
- package/.github/workflows/docker-publish.yml +0 -254
- package/.github/workflows/docs-drift-detector.lock.yml +0 -1142
- package/.github/workflows/docs-drift-detector.md +0 -115
- package/.github/workflows/lint-and-test.yml +0 -60
- package/.github/workflows/publish-npm.yml +0 -85
- package/.github/workflows/secrets-scanning.yml +0 -32
- package/.github/workflows/security-update.yml +0 -127
- package/.gitleaks.toml +0 -9
- package/.prettierignore +0 -21
- package/.prettierrc +0 -33
- package/.scout-ignore +0 -12
- package/.trivyignore +0 -21
- package/CHANGELOG.md +0 -1814
- package/CODE_OF_CONDUCT.md +0 -133
- package/CONTRIBUTING.md +0 -263
- package/DOCKER_README.md +0 -331
- package/Dockerfile +0 -128
- package/SECURITY.md +0 -227
- package/UNRELEASED.md +0 -1
- package/dist/tools-T4U5A3X4.js +0 -2
- package/docker-compose.yml +0 -71
- package/docs/README.md +0 -18
- package/docs/agentic-journal-synergy.md +0 -175
- package/docs/copilot-setup.md +0 -72
- package/eslint.config.js +0 -110
- package/mcp-config-example.json +0 -21
- package/playwright.config.ts +0 -35
- package/releases/v2.1.0.md +0 -220
- package/releases/v2.2.0.md +0 -168
- package/releases/v3.0.0.md +0 -237
- package/releases/v3.1.0.md +0 -104
- package/releases/v3.1.1.md +0 -42
- package/releases/v3.1.2.md +0 -40
- package/releases/v3.1.3.md +0 -64
- package/releases/v3.1.4.md +0 -32
- package/releases/v3.1.5.md +0 -44
- package/releases/v4.0.0.md +0 -71
- package/releases/v4.1.0.md +0 -88
- package/releases/v4.2.0.md +0 -90
- package/releases/v4.3.0.md +0 -92
- package/releases/v4.3.1.md +0 -69
- package/releases/v4.4.0.md +0 -120
- package/releases/v4.4.1.md +0 -33
- package/releases/v4.4.2.md +0 -31
- package/releases/v4.5.0.md +0 -116
- package/releases/v5.0.0.md +0 -105
- package/releases/v5.0.1.md +0 -25
- package/releases/v5.1.0.md +0 -83
- package/releases/v5.1.1.md +0 -10
- package/releases/v6.0.0.md +0 -48
- package/releases/v6.0.1.md +0 -36
- package/releases/v6.1.0.md +0 -68
- package/releases/v6.1.1.md +0 -30
- package/releases/v6.1.2.md +0 -23
- package/scripts/generate-server-instructions.ts +0 -306
- package/scripts/server-instructions-function-body.ts +0 -107
- package/scripts/server-instructions-gotchas.ts +0 -45
- package/server.json +0 -42
- package/social-preview.png +0 -0
- package/src/auth/auth-context.ts +0 -78
- package/src/auth/authorization-server-discovery.ts +0 -263
- package/src/auth/errors.ts +0 -215
- package/src/auth/index.ts +0 -58
- package/src/auth/middleware.ts +0 -392
- package/src/auth/oauth-resource-server.ts +0 -170
- package/src/auth/scope-map.ts +0 -46
- package/src/auth/scopes.ts +0 -256
- package/src/auth/token-validator.ts +0 -293
- package/src/auth/transport-agnostic.ts +0 -164
- package/src/auth/types.ts +0 -372
- package/src/cli.ts +0 -279
- package/src/codemode/api-constants.ts +0 -263
- package/src/codemode/api.ts +0 -302
- package/src/codemode/auto-return.ts +0 -65
- package/src/codemode/index.ts +0 -47
- package/src/codemode/sandbox-factory.ts +0 -144
- package/src/codemode/sandbox.ts +0 -220
- package/src/codemode/security.ts +0 -155
- package/src/codemode/types.ts +0 -228
- package/src/codemode/worker-sandbox.ts +0 -277
- package/src/codemode/worker-script.ts +0 -239
- package/src/constants/icons.ts +0 -183
- package/src/constants/server-instructions.md +0 -166
- package/src/constants/server-instructions.ts +0 -514
- package/src/database/adapter-factory.ts +0 -16
- package/src/database/core/entry-columns.ts +0 -10
- package/src/database/core/interfaces.ts +0 -188
- package/src/database/core/schema.ts +0 -152
- package/src/database/sqlite-adapter/backup.ts +0 -167
- package/src/database/sqlite-adapter/entries/crud.ts +0 -233
- package/src/database/sqlite-adapter/entries/importance.ts +0 -76
- package/src/database/sqlite-adapter/entries/index.ts +0 -142
- package/src/database/sqlite-adapter/entries/search.ts +0 -294
- package/src/database/sqlite-adapter/entries/shared.ts +0 -102
- package/src/database/sqlite-adapter/entries/statistics.ts +0 -162
- package/src/database/sqlite-adapter/index.ts +0 -265
- package/src/database/sqlite-adapter/native-connection.ts +0 -301
- package/src/database/sqlite-adapter/relationships.ts +0 -70
- package/src/database/sqlite-adapter/tags.ts +0 -182
- package/src/filtering/tool-filter.ts +0 -312
- package/src/github/github-integration/client.ts +0 -114
- package/src/github/github-integration/index.ts +0 -297
- package/src/github/github-integration/insights.ts +0 -155
- package/src/github/github-integration/issues.ts +0 -213
- package/src/github/github-integration/milestones.ts +0 -262
- package/src/github/github-integration/projects.ts +0 -414
- package/src/github/github-integration/pull-requests.ts +0 -235
- package/src/github/github-integration/repository.ts +0 -110
- package/src/github/github-integration/types.ts +0 -43
- package/src/handlers/prompts/github.ts +0 -210
- package/src/handlers/prompts/index.ts +0 -97
- package/src/handlers/prompts/workflow.ts +0 -361
- package/src/handlers/resources/core/briefing/context-section.ts +0 -182
- package/src/handlers/resources/core/briefing/github-section.ts +0 -354
- package/src/handlers/resources/core/briefing/index.ts +0 -106
- package/src/handlers/resources/core/briefing/user-message.ts +0 -114
- package/src/handlers/resources/core/health.ts +0 -75
- package/src/handlers/resources/core/index.ts +0 -31
- package/src/handlers/resources/core/instructions.ts +0 -45
- package/src/handlers/resources/core/utilities.ts +0 -310
- package/src/handlers/resources/github.ts +0 -340
- package/src/handlers/resources/graph.ts +0 -218
- package/src/handlers/resources/help.ts +0 -410
- package/src/handlers/resources/index.ts +0 -143
- package/src/handlers/resources/shared.ts +0 -219
- package/src/handlers/resources/team.ts +0 -134
- package/src/handlers/resources/templates.ts +0 -334
- package/src/handlers/tools/admin.ts +0 -351
- package/src/handlers/tools/analytics.ts +0 -346
- package/src/handlers/tools/backup.ts +0 -272
- package/src/handlers/tools/codemode.ts +0 -188
- package/src/handlers/tools/core.ts +0 -359
- package/src/handlers/tools/error-fields-mixin.ts +0 -10
- package/src/handlers/tools/export.ts +0 -150
- package/src/handlers/tools/github/copilot-tools.ts +0 -72
- package/src/handlers/tools/github/helpers.ts +0 -125
- package/src/handlers/tools/github/insights-tools.ts +0 -112
- package/src/handlers/tools/github/issue-tools.ts +0 -442
- package/src/handlers/tools/github/kanban-tools.ts +0 -153
- package/src/handlers/tools/github/milestone-tools.ts +0 -371
- package/src/handlers/tools/github/mutation-tools.ts +0 -17
- package/src/handlers/tools/github/read-tools.ts +0 -302
- package/src/handlers/tools/github/schemas.ts +0 -435
- package/src/handlers/tools/github.ts +0 -39
- package/src/handlers/tools/index.ts +0 -255
- package/src/handlers/tools/relationships.ts +0 -390
- package/src/handlers/tools/schemas.ts +0 -165
- package/src/handlers/tools/search.ts +0 -448
- package/src/handlers/tools/team/admin-tools.ts +0 -164
- package/src/handlers/tools/team/analytics-tools.ts +0 -233
- package/src/handlers/tools/team/backup-tools.ts +0 -83
- package/src/handlers/tools/team/core-tools.ts +0 -197
- package/src/handlers/tools/team/export-tools.ts +0 -130
- package/src/handlers/tools/team/helpers.ts +0 -66
- package/src/handlers/tools/team/index.ts +0 -45
- package/src/handlers/tools/team/relationship-tools.ts +0 -219
- package/src/handlers/tools/team/schemas.ts +0 -558
- package/src/handlers/tools/team/search-tools.ts +0 -145
- package/src/handlers/tools/team/vector-tools.ts +0 -261
- package/src/index.ts +0 -57
- package/src/server/mcp-server.ts +0 -446
- package/src/server/registration.ts +0 -141
- package/src/server/scheduler.ts +0 -283
- package/src/transports/http/handlers.ts +0 -78
- package/src/transports/http/index.ts +0 -8
- package/src/transports/http/security.ts +0 -147
- package/src/transports/http/server/index.ts +0 -397
- package/src/transports/http/server/legacy-sse.ts +0 -87
- package/src/transports/http/server/stateful.ts +0 -222
- package/src/transports/http/server/stateless.ts +0 -42
- package/src/transports/http/types.ts +0 -132
- package/src/types/entities.ts +0 -145
- package/src/types/error-types.ts +0 -92
- package/src/types/errors.ts +0 -200
- package/src/types/filtering.ts +0 -55
- package/src/types/github.ts +0 -216
- package/src/types/index.ts +0 -348
- package/src/utils/error-helpers.ts +0 -78
- package/src/utils/errors/error-response-fields.ts +0 -29
- package/src/utils/errors/suggestions.ts +0 -94
- package/src/utils/github-helpers.ts +0 -33
- package/src/utils/logger.ts +0 -107
- package/src/utils/mcp-logger.ts +0 -155
- package/src/utils/progress-utils.ts +0 -100
- package/src/utils/query-helpers.ts +0 -78
- package/src/utils/resource-annotations.ts +0 -75
- package/src/utils/security-utils.ts +0 -198
- package/src/utils/vector-index-helpers.ts +0 -24
- package/src/vector/vector-search-manager.ts +0 -409
- package/src/version.ts +0 -15
- package/test-server/README.md +0 -193
- package/test-server/code-map.md +0 -399
- package/test-server/test-agent-experience.md +0 -213
- package/test-server/test-filter-instructions.mjs +0 -295
- package/test-server/test-instruction-levels.mjs +0 -102
- package/test-server/test-preflight.md +0 -55
- package/test-server/test-prompts.mjs +0 -185
- package/test-server/test-scheduler.mjs +0 -174
- package/test-server/test-tool-annotations.mjs +0 -115
- package/test-server/test-tools-codemode.md +0 -632
- package/test-server/test-tools-codemode2.md +0 -1218
- package/test-server/test-tools-team.md +0 -215
- package/test-server/test-tools.md +0 -429
- package/test-server/test-tools2.md +0 -361
- package/test-server/test-tools3.md +0 -396
- package/test-server/tool-reference.md +0 -231
- package/tests/README.md +0 -54
- package/tests/auth/auth-context.test.ts +0 -162
- package/tests/auth/authorization-server-discovery.test.ts +0 -265
- package/tests/auth/errors.test.ts +0 -170
- package/tests/auth/middleware.test.ts +0 -585
- package/tests/auth/oauth-resource-server.test.ts +0 -173
- package/tests/auth/scope-map.test.ts +0 -66
- package/tests/auth/scopes.test.ts +0 -347
- package/tests/auth/token-validator.test.ts +0 -271
- package/tests/codemode/api.test.ts +0 -396
- package/tests/codemode/auto-return.test.ts +0 -167
- package/tests/codemode/codemode-tool-handlers.test.ts +0 -197
- package/tests/codemode/sandbox-factory.test.ts +0 -152
- package/tests/codemode/sandbox.test.ts +0 -190
- package/tests/codemode/security.test.ts +0 -242
- package/tests/codemode/worker-sandbox.test.ts +0 -106
- package/tests/constants/icons.test.ts +0 -101
- package/tests/constants/server-instructions.test.ts +0 -514
- package/tests/database/crud-workflow-branches.test.ts +0 -418
- package/tests/database/database-branches.test.ts +0 -132
- package/tests/database/entries-auth-branches.test.ts +0 -390
- package/tests/database/native-connection.test.ts +0 -249
- package/tests/database/shared-helpers.test.ts +0 -103
- package/tests/database/sqlite-adapter.bench.ts +0 -63
- package/tests/database/sqlite-adapter.test.ts +0 -690
- package/tests/database/tags.test.ts +0 -134
- package/tests/e2e/README.md +0 -39
- package/tests/e2e/auth.spec.ts +0 -106
- package/tests/e2e/codemode-abuse.spec.ts +0 -75
- package/tests/e2e/health.spec.ts +0 -63
- package/tests/e2e/helpers.ts +0 -139
- package/tests/e2e/oauth-discovery.spec.ts +0 -102
- package/tests/e2e/oauth-scopes.spec.ts +0 -222
- package/tests/e2e/payloads-admin.spec.ts +0 -76
- package/tests/e2e/payloads-analytics.spec.ts +0 -37
- package/tests/e2e/payloads-backup-restore.spec.ts +0 -102
- package/tests/e2e/payloads-backup.spec.ts +0 -44
- package/tests/e2e/payloads-codemode-api.spec.ts +0 -131
- package/tests/e2e/payloads-codemode-readonly.spec.ts +0 -116
- package/tests/e2e/payloads-codemode.spec.ts +0 -116
- package/tests/e2e/payloads-core.spec.ts +0 -82
- package/tests/e2e/payloads-error-contracts.spec.ts +0 -159
- package/tests/e2e/payloads-export.spec.ts +0 -46
- package/tests/e2e/payloads-github-degradation.spec.ts +0 -73
- package/tests/e2e/payloads-github.spec.ts +0 -176
- package/tests/e2e/payloads-relationships.spec.ts +0 -56
- package/tests/e2e/payloads-search.spec.ts +0 -64
- package/tests/e2e/payloads-team-happy.spec.ts +0 -231
- package/tests/e2e/payloads-team.spec.ts +0 -174
- package/tests/e2e/prompts-expanded.spec.ts +0 -137
- package/tests/e2e/prompts.spec.ts +0 -62
- package/tests/e2e/protocols.spec.ts +0 -134
- package/tests/e2e/rate-limiting.spec.ts +0 -291
- package/tests/e2e/resources-briefing-env.spec.ts +0 -106
- package/tests/e2e/resources-complete.spec.ts +0 -180
- package/tests/e2e/resources-expanded.spec.ts +0 -83
- package/tests/e2e/resources-instructions-levels.spec.ts +0 -145
- package/tests/e2e/resources-templates.spec.ts +0 -123
- package/tests/e2e/resources.spec.ts +0 -103
- package/tests/e2e/scheduler.spec.ts +0 -79
- package/tests/e2e/security.spec.ts +0 -112
- package/tests/e2e/session-advanced.spec.ts +0 -152
- package/tests/e2e/sessions.spec.ts +0 -95
- package/tests/e2e/stateless.spec.ts +0 -79
- package/tests/e2e/streaming.spec.ts +0 -176
- package/tests/e2e/tool-filtering-presets.spec.ts +0 -192
- package/tests/e2e/tool-filtering.spec.ts +0 -77
- package/tests/e2e/tools.spec.ts +0 -111
- package/tests/filtering/tool-filter.test.ts +0 -314
- package/tests/github/client-issues-errors.test.ts +0 -433
- package/tests/github/github-integration-branches.test.ts +0 -490
- package/tests/github/github-integration.test.ts +0 -1015
- package/tests/github/github-managers-branches.test.ts +0 -907
- package/tests/github/pull-requests.test.ts +0 -334
- package/tests/handlers/analytics-branches.test.ts +0 -222
- package/tests/handlers/backup-branches.test.ts +0 -270
- package/tests/handlers/briefing-context-section.test.ts +0 -388
- package/tests/handlers/briefing-github-section.test.ts +0 -392
- package/tests/handlers/briefing-user-message.test.ts +0 -405
- package/tests/handlers/codemode-tools.test.ts +0 -85
- package/tests/handlers/copilot-tools.test.ts +0 -126
- package/tests/handlers/error-path-coverage.test.ts +0 -324
- package/tests/handlers/export-tools.test.ts +0 -203
- package/tests/handlers/github-resource-handlers.test.ts +0 -929
- package/tests/handlers/github-tool-handlers.test.ts +0 -1452
- package/tests/handlers/handler-error-branches.test.ts +0 -346
- package/tests/handlers/help-resource.test.ts +0 -92
- package/tests/handlers/prompt-handler-coverage.test.ts +0 -108
- package/tests/handlers/prompt-handlers.test.ts +0 -131
- package/tests/handlers/resource-handler-coverage.test.ts +0 -281
- package/tests/handlers/resource-handlers.test.ts +0 -357
- package/tests/handlers/resource-prompt-branches.test.ts +0 -495
- package/tests/handlers/search-tool-handlers.test.ts +0 -379
- package/tests/handlers/targeted-gap-closure.test.ts +0 -387
- package/tests/handlers/team-admin.test.ts +0 -291
- package/tests/handlers/team-analytics.test.ts +0 -220
- package/tests/handlers/team-core.test.ts +0 -148
- package/tests/handlers/team-data.test.ts +0 -198
- package/tests/handlers/team-relationships.test.ts +0 -271
- package/tests/handlers/team-resource-handlers.test.ts +0 -161
- package/tests/handlers/team-search.test.ts +0 -134
- package/tests/handlers/team-tool-handlers.test.ts +0 -301
- package/tests/handlers/team-vector.test.ts +0 -213
- package/tests/handlers/template-github-branches.test.ts +0 -676
- package/tests/handlers/tool-annotations.test.ts +0 -90
- package/tests/handlers/tool-handler-coverage.test.ts +0 -514
- package/tests/handlers/tool-handlers.test.ts +0 -510
- package/tests/handlers/tool-output-schemas.test.ts +0 -116
- package/tests/handlers/vector-tool-handlers.test.ts +0 -238
- package/tests/security/sql-injection.test.ts +0 -284
- package/tests/server/mcp-server.bench.ts +0 -55
- package/tests/server/mcp-server.test.ts +0 -1326
- package/tests/server/scheduler.test.ts +0 -400
- package/tests/transports/http-legacy-sse.test.ts +0 -275
- package/tests/transports/http-security.test.ts +0 -322
- package/tests/transports/http-stateful.test.ts +0 -487
- package/tests/transports/http-transport-server.test.ts +0 -301
- package/tests/transports/http-transport.test.ts +0 -771
- package/tests/utils/github-helpers.test.ts +0 -58
- package/tests/utils/logger.test.ts +0 -180
- package/tests/utils/mcp-logger.test.ts +0 -211
- package/tests/utils/progress-utils.test.ts +0 -156
- package/tests/utils/query-helpers.test.ts +0 -80
- package/tests/utils/security-utils.test.ts +0 -82
- package/tests/vector/vector-search-branches.test.ts +0 -111
- package/tests/vector/vector-search-manager.test.ts +0 -375
- package/tests/vector/vector-search.bench.ts +0 -48
- package/tsconfig.json +0 -42
- package/tsup.config.ts +0 -19
- package/vitest.config.ts +0 -25
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
-
import Database from 'better-sqlite3'
|
|
3
|
-
import { NativeConnectionManager } from '../../src/database/sqlite-adapter/native-connection.js'
|
|
4
|
-
import { TagsManager } from '../../src/database/sqlite-adapter/tags.js'
|
|
5
|
-
|
|
6
|
-
vi.mock('../../src/utils/logger.js', () => ({
|
|
7
|
-
logger: { info: vi.fn(), warning: vi.fn(), error: vi.fn(), debug: vi.fn() },
|
|
8
|
-
}))
|
|
9
|
-
|
|
10
|
-
function createTagsManager(): { conn: NativeConnectionManager; manager: TagsManager } {
|
|
11
|
-
const conn = new NativeConnectionManager(':memory:')
|
|
12
|
-
const db = new Database(':memory:')
|
|
13
|
-
|
|
14
|
-
db.exec(`
|
|
15
|
-
CREATE TABLE IF NOT EXISTS memory_journal (
|
|
16
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT
|
|
17
|
-
);
|
|
18
|
-
CREATE TABLE IF NOT EXISTS tags (
|
|
19
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
|
-
name TEXT UNIQUE NOT NULL,
|
|
21
|
-
usage_count INTEGER DEFAULT 0
|
|
22
|
-
);
|
|
23
|
-
CREATE TABLE IF NOT EXISTS entry_tags (
|
|
24
|
-
entry_id INTEGER NOT NULL,
|
|
25
|
-
tag_id INTEGER NOT NULL,
|
|
26
|
-
PRIMARY KEY(entry_id, tag_id)
|
|
27
|
-
);
|
|
28
|
-
`)
|
|
29
|
-
|
|
30
|
-
Object.assign(conn, { db, initialized: true })
|
|
31
|
-
const manager = new TagsManager(conn)
|
|
32
|
-
return { conn, manager }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe('TagsManager', () => {
|
|
36
|
-
let conn: NativeConnectionManager
|
|
37
|
-
let manager: TagsManager
|
|
38
|
-
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
vi.clearAllMocks()
|
|
41
|
-
const setup = createTagsManager()
|
|
42
|
-
conn = setup.conn
|
|
43
|
-
manager = setup.manager
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
afterEach(() => {
|
|
47
|
-
conn.close()
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('should link tags to entry', () => {
|
|
51
|
-
const db = conn.getRawDb() as Database
|
|
52
|
-
db.prepare('INSERT INTO memory_journal (id) VALUES (1)').run()
|
|
53
|
-
|
|
54
|
-
manager.linkTagsToEntry(1, ['tag1', 'tag2'])
|
|
55
|
-
|
|
56
|
-
const tags = manager.getTagsForEntry(1)
|
|
57
|
-
expect(tags).toContain('tag1')
|
|
58
|
-
expect(tags).toContain('tag2')
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('should ignore linking zero tags', () => {
|
|
62
|
-
expect(() => manager.linkTagsToEntry(1, [])).not.toThrow()
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('should support batch retrieval', () => {
|
|
66
|
-
const db = conn.getRawDb() as Database
|
|
67
|
-
db.prepare('INSERT INTO memory_journal (id) VALUES (1)').run()
|
|
68
|
-
db.prepare('INSERT INTO memory_journal (id) VALUES (2)').run()
|
|
69
|
-
|
|
70
|
-
manager.linkTagsToEntry(1, ['tag1'])
|
|
71
|
-
manager.linkTagsToEntry(2, ['tag1', 'tag2'])
|
|
72
|
-
|
|
73
|
-
const map = manager.batchGetTagsForEntries([1, 2])
|
|
74
|
-
expect(map.get(1)).toContain('tag1')
|
|
75
|
-
expect(map.get(2)).toContain('tag2')
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('should support empty array batch get', () => {
|
|
79
|
-
const map = manager.batchGetTagsForEntries([])
|
|
80
|
-
expect(map.size).toBe(0)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it('should list tags ordered by usage', () => {
|
|
84
|
-
const db = conn.getRawDb() as Database
|
|
85
|
-
db.prepare('INSERT INTO memory_journal (id) VALUES (1)').run()
|
|
86
|
-
manager.linkTagsToEntry(1, ['tag1', 'tag2'])
|
|
87
|
-
|
|
88
|
-
const tags = manager.listTags()
|
|
89
|
-
expect(tags.length).toBe(2)
|
|
90
|
-
expect(tags[0]!.name).toBeDefined()
|
|
91
|
-
expect(tags[0]!.usageCount).toBe(1)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('should merge tags properly', () => {
|
|
95
|
-
const db = conn.getRawDb() as Database
|
|
96
|
-
db.prepare('INSERT INTO memory_journal (id) VALUES (1)').run()
|
|
97
|
-
manager.linkTagsToEntry(1, ['sourceTag'])
|
|
98
|
-
|
|
99
|
-
const result = manager.mergeTags('sourceTag', 'targetTag')
|
|
100
|
-
expect(result.entriesUpdated).toBe(1)
|
|
101
|
-
expect(result.sourceDeleted).toBe(true)
|
|
102
|
-
|
|
103
|
-
const tags = manager.getTagsForEntry(1)
|
|
104
|
-
expect(tags).not.toContain('sourceTag')
|
|
105
|
-
expect(tags).toContain('targetTag')
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('should merge tags when target already has it but ignore duplicates', () => {
|
|
109
|
-
const db = conn.getRawDb() as Database
|
|
110
|
-
db.prepare('INSERT INTO memory_journal (id) VALUES (1)').run()
|
|
111
|
-
db.prepare('INSERT INTO memory_journal (id) VALUES (2)').run()
|
|
112
|
-
|
|
113
|
-
manager.linkTagsToEntry(1, ['sourceTag', 'targetTag'])
|
|
114
|
-
manager.linkTagsToEntry(2, ['sourceTag'])
|
|
115
|
-
|
|
116
|
-
const result = manager.mergeTags('sourceTag', 'targetTag')
|
|
117
|
-
expect(result.entriesUpdated).toBe(1) // only entry 2 updated
|
|
118
|
-
expect(manager.getTagsForEntry(1)).toContain('targetTag')
|
|
119
|
-
expect(manager.getTagsForEntry(2)).toContain('targetTag')
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('should handle merge when target tag does not exist yet', () => {
|
|
123
|
-
const db = conn.getRawDb() as Database
|
|
124
|
-
db.prepare('INSERT INTO memory_journal (id) VALUES (1)').run()
|
|
125
|
-
manager.linkTagsToEntry(1, ['sourceTag'])
|
|
126
|
-
|
|
127
|
-
manager.mergeTags('sourceTag', 'newTargetTag')
|
|
128
|
-
expect(manager.getTagsForEntry(1)).toContain('newTargetTag')
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('should throw on merging a non-existent tag', () => {
|
|
132
|
-
expect(() => manager.mergeTags('ghostTag', 'targetTag')).toThrow(/not found/i)
|
|
133
|
-
})
|
|
134
|
-
})
|
package/tests/e2e/README.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Memory Journal MCP - Playwright Suite (E2E)
|
|
2
|
-
|
|
3
|
-
> **⚠️ AI AGENT INSTRUCTIONS:** Read these rules before creating, modifying, or running tests in this directory.
|
|
4
|
-
|
|
5
|
-
## 📌 Framework & Purpose
|
|
6
|
-
|
|
7
|
-
- **Framework:** Playwright (`playwright.config.ts`)
|
|
8
|
-
- **Purpose:** End-to-End (E2E) protocol, transport, and integration parity testing.
|
|
9
|
-
- **Focus:** HTTP/SSE transport, sessions, CORS, security headers, rate limiting, stateless mode, and automated schedulers.
|
|
10
|
-
- **Database:** Uses a dedicated isolated test DB (`.test-output/e2e/test-e2e.db`).
|
|
11
|
-
|
|
12
|
-
## 🚨 AI Agent Testing Rules
|
|
13
|
-
|
|
14
|
-
### 1. Scope of E2E Tests
|
|
15
|
-
|
|
16
|
-
- **DO NOT** write tests here to verify core handler logic (e.g., does `create_entry` work in SQLite?). That belongs in the Vitest suite (`tests/README.md`).
|
|
17
|
-
- **DO** write tests here to ensure the complete HTTP system correctly negotiates protocols, streams SSE events, handles standard MCP payloads, manages stateless instances, or evaluates server-level behaviors.
|
|
18
|
-
|
|
19
|
-
### 2. Execution Protocol
|
|
20
|
-
|
|
21
|
-
- Run via npm scripts: `npm run test:e2e`
|
|
22
|
-
- For targeted test files: `npx playwright test tests/e2e/your-file.spec.ts`
|
|
23
|
-
- Playwright automatically handles spinning up the development server via the `webServer` config in `playwright.config.ts`.
|
|
24
|
-
- Check output carefully to verify that the server started successfully before tests executed.
|
|
25
|
-
|
|
26
|
-
### 3. File Naming & Structure
|
|
27
|
-
|
|
28
|
-
- Naming convention: `<feature>.spec.ts` (e.g., `payloads-admin.spec.ts`, `health.spec.ts`, `streaming.spec.ts`).
|
|
29
|
-
- **Never** use PascalCase or camelCase. Use `kebab-case`.
|
|
30
|
-
- Helper functions belong in `tests/e2e/helpers.ts`. Do not duplicate initialization and HTTP request logic across tests. Use the provided helpers for MCP handshakes, session initialization, and request execution.
|
|
31
|
-
|
|
32
|
-
### 4. Flakiness & Determinism
|
|
33
|
-
|
|
34
|
-
- Do not rely on fixed `setTimeout()` unless absolutely necessary. Rely on Playwright's auto-retry assertions (e.g., `expect.poll`).
|
|
35
|
-
- Tests should clean up their state or be isolated from one another. Wait for scheduled intervals to conclude securely or manage time appropriately.
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
_For a complete code map and directory breakdown, see `test-server/code-map.md`._
|
package/tests/e2e/auth.spec.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E2E Tests: Bearer Token Authentication
|
|
3
|
-
*
|
|
4
|
-
* Tests the --auth-token middleware. Uses a test-local server
|
|
5
|
-
* on port 3101 to avoid conflicting with the main webServer.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { test, expect } from '@playwright/test'
|
|
9
|
-
import { startServer, stopServer } from './helpers.js'
|
|
10
|
-
|
|
11
|
-
const AUTH_TOKEN = 'test-secret-token-e2e'
|
|
12
|
-
const AUTH_PORT = 3101
|
|
13
|
-
const AUTH_BASE = `http://localhost:${AUTH_PORT}`
|
|
14
|
-
|
|
15
|
-
test.describe('Bearer Token Authentication', () => {
|
|
16
|
-
test.beforeAll(async () => {
|
|
17
|
-
await startServer(AUTH_PORT, ['--auth-token', AUTH_TOKEN], 'auth')
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
test.afterAll(() => {
|
|
21
|
-
stopServer(AUTH_PORT)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
test('/health should be accessible without token (exempt)', async () => {
|
|
25
|
-
const response = await fetch(`${AUTH_BASE}/health`)
|
|
26
|
-
expect(response.status).toBe(200)
|
|
27
|
-
|
|
28
|
-
const body = await response.json()
|
|
29
|
-
expect(body).toHaveProperty('status', 'healthy')
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
test('POST /mcp should return 401 without Authorization header', async () => {
|
|
33
|
-
const response = await fetch(`${AUTH_BASE}/mcp`, {
|
|
34
|
-
method: 'POST',
|
|
35
|
-
headers: {
|
|
36
|
-
'Content-Type': 'application/json',
|
|
37
|
-
Accept: 'application/json, text/event-stream',
|
|
38
|
-
},
|
|
39
|
-
body: JSON.stringify({
|
|
40
|
-
jsonrpc: '2.0',
|
|
41
|
-
id: 1,
|
|
42
|
-
method: 'initialize',
|
|
43
|
-
params: {
|
|
44
|
-
protocolVersion: '2025-03-26',
|
|
45
|
-
capabilities: {},
|
|
46
|
-
clientInfo: { name: 'test', version: '1.0' },
|
|
47
|
-
},
|
|
48
|
-
}),
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
expect(response.status).toBe(401)
|
|
52
|
-
const body = await response.json()
|
|
53
|
-
expect(body).toHaveProperty('error', 'Unauthorized')
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
test('POST /mcp should return 401 with wrong token', async () => {
|
|
57
|
-
const response = await fetch(`${AUTH_BASE}/mcp`, {
|
|
58
|
-
method: 'POST',
|
|
59
|
-
headers: {
|
|
60
|
-
'Content-Type': 'application/json',
|
|
61
|
-
Accept: 'application/json, text/event-stream',
|
|
62
|
-
Authorization: 'Bearer wrong-token',
|
|
63
|
-
},
|
|
64
|
-
body: JSON.stringify({
|
|
65
|
-
jsonrpc: '2.0',
|
|
66
|
-
id: 1,
|
|
67
|
-
method: 'initialize',
|
|
68
|
-
params: {
|
|
69
|
-
protocolVersion: '2025-03-26',
|
|
70
|
-
capabilities: {},
|
|
71
|
-
clientInfo: { name: 'test', version: '1.0' },
|
|
72
|
-
},
|
|
73
|
-
}),
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
expect(response.status).toBe(401)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
test('POST /mcp should succeed with correct Bearer token', async () => {
|
|
80
|
-
const response = await fetch(`${AUTH_BASE}/mcp`, {
|
|
81
|
-
method: 'POST',
|
|
82
|
-
headers: {
|
|
83
|
-
'Content-Type': 'application/json',
|
|
84
|
-
Accept: 'application/json, text/event-stream',
|
|
85
|
-
Authorization: `Bearer ${AUTH_TOKEN}`,
|
|
86
|
-
},
|
|
87
|
-
body: JSON.stringify({
|
|
88
|
-
jsonrpc: '2.0',
|
|
89
|
-
id: 1,
|
|
90
|
-
method: 'initialize',
|
|
91
|
-
params: {
|
|
92
|
-
protocolVersion: '2025-03-26',
|
|
93
|
-
capabilities: {},
|
|
94
|
-
clientInfo: { name: 'test', version: '1.0' },
|
|
95
|
-
},
|
|
96
|
-
}),
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
expect(response.status).toBe(200)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test('GET / should return 401 without token', async () => {
|
|
103
|
-
const response = await fetch(`${AUTH_BASE}/`)
|
|
104
|
-
expect(response.status).toBe(401)
|
|
105
|
-
})
|
|
106
|
-
})
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E2E Tests: Code Mode Abuse & Timeouts
|
|
3
|
-
*
|
|
4
|
-
* Tests the worker sandbox's ability to terminate hung operations
|
|
5
|
-
* (infinite loops, unresolving promises) without affecting the
|
|
6
|
-
* HTTP transport layer or crashing the server.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { test, expect } from '@playwright/test'
|
|
10
|
-
import type { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
11
|
-
import { createClient } from './helpers.js'
|
|
12
|
-
|
|
13
|
-
test.describe.configure({ mode: 'serial' })
|
|
14
|
-
|
|
15
|
-
test.describe('Payload Contracts: Code Mode Abuse', () => {
|
|
16
|
-
let client: Client
|
|
17
|
-
|
|
18
|
-
test.beforeAll(async () => {
|
|
19
|
-
client = await createClient()
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
test.afterAll(async () => {
|
|
23
|
-
await client.close()
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
test('mj_execute_code with infinite loop aborts via timeout instead of hanging HTTP', async () => {
|
|
27
|
-
const response = await client.callTool({
|
|
28
|
-
name: 'mj_execute_code',
|
|
29
|
-
arguments: {
|
|
30
|
-
code: 'while(true) {}',
|
|
31
|
-
timeout: 50, // Very sharp timeout for the test
|
|
32
|
-
},
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
expect(Array.isArray(response.content)).toBe(true)
|
|
36
|
-
const payload = JSON.parse((response.content as Array<{ text: string }>)[0]!.text)
|
|
37
|
-
|
|
38
|
-
expect(payload.success).toBe(false)
|
|
39
|
-
expect(payload.error).toContain('timed out')
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
test('mj_execute_code with unresolving promise aborts via timeout', async () => {
|
|
43
|
-
const response = await client.callTool({
|
|
44
|
-
name: 'mj_execute_code',
|
|
45
|
-
arguments: {
|
|
46
|
-
code: 'await new Promise(() => {})', // Never resolves
|
|
47
|
-
timeout: 50,
|
|
48
|
-
},
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
expect(Array.isArray(response.content)).toBe(true)
|
|
52
|
-
const payload = JSON.parse((response.content as Array<{ text: string }>)[0]!.text)
|
|
53
|
-
|
|
54
|
-
expect(payload.success).toBe(false)
|
|
55
|
-
// Unresolving promise causes worker exit (code 1) rather than the named timeout path
|
|
56
|
-
const isTimeoutError = /timed out|Worker exited/i.test(String(payload.error))
|
|
57
|
-
expect(isTimeoutError).toBe(true)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
test('server handles subsequent valid Code Mode requests normally after worker drop', async () => {
|
|
61
|
-
// This proves the transport stays up and worker pool recovers
|
|
62
|
-
const response = await client.callTool({
|
|
63
|
-
name: 'mj_execute_code',
|
|
64
|
-
arguments: {
|
|
65
|
-
code: 'return 1 + 1', // Must use return; code is wrapped in (async () => { ${code} })()
|
|
66
|
-
},
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
expect(Array.isArray(response.content)).toBe(true)
|
|
70
|
-
const payload = JSON.parse((response.content as Array<{ text: string }>)[0]!.text)
|
|
71
|
-
|
|
72
|
-
expect(payload.success).toBe(true)
|
|
73
|
-
expect(payload.result).toBe(2)
|
|
74
|
-
})
|
|
75
|
-
})
|
package/tests/e2e/health.spec.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E2E Tests: Health & Root Info
|
|
3
|
-
*
|
|
4
|
-
* Verifies the HTTP server's health check and root info endpoints
|
|
5
|
-
* return correct responses with expected structure.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { test, expect } from '@playwright/test'
|
|
9
|
-
|
|
10
|
-
test.describe('Health & Root Info', () => {
|
|
11
|
-
test('should return 200 OK from /health endpoint', async ({ request }) => {
|
|
12
|
-
const response = await request.get('/health')
|
|
13
|
-
expect(response.status()).toBe(200)
|
|
14
|
-
|
|
15
|
-
const body = await response.json()
|
|
16
|
-
expect(body).toHaveProperty('status', 'healthy')
|
|
17
|
-
expect(body).toHaveProperty('timestamp')
|
|
18
|
-
// Timestamp should be a valid ISO string
|
|
19
|
-
expect(new Date(body.timestamp).toISOString()).toBe(body.timestamp)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
test('should return server metadata on GET /', async ({ request }) => {
|
|
23
|
-
const response = await request.get('/')
|
|
24
|
-
expect(response.status()).toBe(200)
|
|
25
|
-
|
|
26
|
-
const body = await response.json()
|
|
27
|
-
expect(body).toHaveProperty('name', 'memory-journal-mcp')
|
|
28
|
-
expect(body).toHaveProperty('version')
|
|
29
|
-
expect(body).toHaveProperty('description')
|
|
30
|
-
expect(body).toHaveProperty('endpoints')
|
|
31
|
-
expect(body.endpoints).toHaveProperty('POST /mcp')
|
|
32
|
-
expect(body.endpoints).toHaveProperty('GET /sse')
|
|
33
|
-
expect(body.endpoints).toHaveProperty('GET /health')
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
test('should accept MCP initialization request on /mcp', async ({ request }) => {
|
|
37
|
-
const response = await request.post('/mcp', {
|
|
38
|
-
headers: {
|
|
39
|
-
Accept: 'application/json, text/event-stream',
|
|
40
|
-
},
|
|
41
|
-
data: {
|
|
42
|
-
jsonrpc: '2.0',
|
|
43
|
-
id: 1,
|
|
44
|
-
method: 'initialize',
|
|
45
|
-
params: {
|
|
46
|
-
protocolVersion: '2025-03-26',
|
|
47
|
-
capabilities: {},
|
|
48
|
-
clientInfo: {
|
|
49
|
-
name: 'playwright-test',
|
|
50
|
-
version: '1.0.0',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
expect(response.status()).toBe(200)
|
|
57
|
-
// Session ID should be returned in response headers
|
|
58
|
-
const sessionId = response.headers()['mcp-session-id']
|
|
59
|
-
expect(sessionId).toBeDefined()
|
|
60
|
-
expect(typeof sessionId).toBe('string')
|
|
61
|
-
expect(sessionId!.length).toBeGreaterThan(0)
|
|
62
|
-
})
|
|
63
|
-
})
|
package/tests/e2e/helpers.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared E2E test helpers for payload contract tests.
|
|
3
|
-
*
|
|
4
|
-
* Provides:
|
|
5
|
-
* - createClient() — Streamable HTTP client factory
|
|
6
|
-
* - callToolAndParse() — call tool + parse JSON response
|
|
7
|
-
* - expectSuccess() — assert payload is not a structured error
|
|
8
|
-
* - startServer() / stopServer() — managed child-process server lifecycle
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
12
|
-
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
|
13
|
-
import { expect } from '@playwright/test'
|
|
14
|
-
import { type ChildProcess, spawn } from 'node:child_process'
|
|
15
|
-
import { setTimeout as delay } from 'node:timers/promises'
|
|
16
|
-
import { join } from 'node:path'
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Create and connect a Streamable HTTP MCP client.
|
|
20
|
-
* Caller is responsible for calling client.close() in afterAll.
|
|
21
|
-
*/
|
|
22
|
-
export async function createClient(port = 3100): Promise<Client> {
|
|
23
|
-
const transport = new StreamableHTTPClientTransport(new URL(`http://localhost:${port}/mcp`))
|
|
24
|
-
const client = new Client(
|
|
25
|
-
{ name: 'payload-contract-test', version: '1.0.0' },
|
|
26
|
-
{ capabilities: {} }
|
|
27
|
-
)
|
|
28
|
-
await client.connect(transport)
|
|
29
|
-
return client
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Call a tool and parse the JSON text response into a typed object.
|
|
34
|
-
* Asserts the response has text content and returns the parsed payload.
|
|
35
|
-
*/
|
|
36
|
-
export async function callToolAndParse(
|
|
37
|
-
client: Client,
|
|
38
|
-
toolName: string,
|
|
39
|
-
args: Record<string, unknown>
|
|
40
|
-
): Promise<Record<string, unknown>> {
|
|
41
|
-
const response = await client.callTool({ name: toolName, arguments: args })
|
|
42
|
-
|
|
43
|
-
expect(response.isError).toBeUndefined()
|
|
44
|
-
expect(Array.isArray(response.content)).toBe(true)
|
|
45
|
-
|
|
46
|
-
const content = response.content as Array<{ type: string; text?: string }>
|
|
47
|
-
expect(content.length).toBeGreaterThan(0)
|
|
48
|
-
|
|
49
|
-
const first = content[0]!
|
|
50
|
-
expect(first.type).toBe('text')
|
|
51
|
-
|
|
52
|
-
return JSON.parse(first.text!) as Record<string, unknown>
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Assert that a parsed payload is NOT a structured error.
|
|
57
|
-
* Throws a descriptive error if the tool returned { success: false, error: "..." }.
|
|
58
|
-
*/
|
|
59
|
-
export function expectSuccess(payload: Record<string, unknown>): void {
|
|
60
|
-
if (payload.success === false) {
|
|
61
|
-
throw new Error(`Tool returned error: ${JSON.stringify(payload.error)}`)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// =============================================================================
|
|
66
|
-
// Server Process Management
|
|
67
|
-
// =============================================================================
|
|
68
|
-
|
|
69
|
-
interface ManagedServer {
|
|
70
|
-
process: ChildProcess
|
|
71
|
-
port: number
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const managedServers = new Map<number, ManagedServer>()
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Start an MCP server as a child process with custom CLI args.
|
|
78
|
-
* Waits for /health to become reachable before returning.
|
|
79
|
-
*
|
|
80
|
-
* @param port - Port to start the server on
|
|
81
|
-
* @param args - Additional CLI args (e.g., ['--stateless', '--auth-token', 'secret'])
|
|
82
|
-
* @param dbSuffix - Database file suffix to avoid collisions (default: port number)
|
|
83
|
-
*/
|
|
84
|
-
export async function startServer(
|
|
85
|
-
port: number,
|
|
86
|
-
args: string[] = [],
|
|
87
|
-
dbSuffix?: string,
|
|
88
|
-
options?: { cwd?: string }
|
|
89
|
-
): Promise<void> {
|
|
90
|
-
const suffix = dbSuffix ?? String(port)
|
|
91
|
-
const serverProcess = spawn(
|
|
92
|
-
'node',
|
|
93
|
-
[
|
|
94
|
-
join(process.cwd(), 'dist/cli.js'),
|
|
95
|
-
'--transport',
|
|
96
|
-
'http',
|
|
97
|
-
'--port',
|
|
98
|
-
String(port),
|
|
99
|
-
'--db',
|
|
100
|
-
join(process.cwd(), '.test-output', 'e2e', `test-e2e-${suffix}.db`),
|
|
101
|
-
...args,
|
|
102
|
-
],
|
|
103
|
-
{
|
|
104
|
-
cwd: options?.cwd ?? process.cwd(),
|
|
105
|
-
stdio: 'pipe',
|
|
106
|
-
env: {
|
|
107
|
-
...process.env,
|
|
108
|
-
MCP_RATE_LIMIT_MAX: args.some((a) => a === '--rate-limit-max')
|
|
109
|
-
? undefined
|
|
110
|
-
: '10000',
|
|
111
|
-
},
|
|
112
|
-
}
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
managedServers.set(port, { process: serverProcess, port })
|
|
116
|
-
|
|
117
|
-
const maxAttempts = 30
|
|
118
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
119
|
-
try {
|
|
120
|
-
const res = await fetch(`http://localhost:${port}/health`)
|
|
121
|
-
if (res.ok) return
|
|
122
|
-
} catch {
|
|
123
|
-
// Server not ready yet
|
|
124
|
-
}
|
|
125
|
-
await delay(500)
|
|
126
|
-
}
|
|
127
|
-
throw new Error(`Server on port ${port} did not start within timeout`)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Stop a managed server by port number.
|
|
132
|
-
*/
|
|
133
|
-
export function stopServer(port: number): void {
|
|
134
|
-
const server = managedServers.get(port)
|
|
135
|
-
if (server) {
|
|
136
|
-
server.process.kill('SIGTERM')
|
|
137
|
-
managedServers.delete(port)
|
|
138
|
-
}
|
|
139
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E2E Tests: OAuth 2.1 Discovery Endpoint
|
|
3
|
-
*
|
|
4
|
-
* Tests the RFC 9728 Protected Resource Metadata endpoint
|
|
5
|
-
* (/.well-known/oauth-protected-resource) behavior with
|
|
6
|
-
* and without OAuth enabled.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { test, expect } from '@playwright/test'
|
|
10
|
-
import { startServer, stopServer } from './helpers.js'
|
|
11
|
-
|
|
12
|
-
const OAUTH_PORT = 3105
|
|
13
|
-
|
|
14
|
-
test.describe('OAuth 2.1 Discovery', () => {
|
|
15
|
-
test.describe('Without OAuth enabled (default)', () => {
|
|
16
|
-
test('/.well-known/oauth-protected-resource should return 404', async ({ request }) => {
|
|
17
|
-
// Default webServer on port 3100 does not have OAuth enabled
|
|
18
|
-
const response = await request.get('/.well-known/oauth-protected-resource')
|
|
19
|
-
|
|
20
|
-
// Without OAuth, the endpoint should not be registered
|
|
21
|
-
expect(response.status()).toBe(404)
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test.describe('With OAuth enabled', () => {
|
|
26
|
-
test.beforeAll(async () => {
|
|
27
|
-
await startServer(
|
|
28
|
-
OAUTH_PORT,
|
|
29
|
-
[
|
|
30
|
-
'--oauth-enabled',
|
|
31
|
-
'--oauth-issuer',
|
|
32
|
-
'https://auth.example.com/realms/test',
|
|
33
|
-
'--oauth-audience',
|
|
34
|
-
'memory-journal-mcp',
|
|
35
|
-
],
|
|
36
|
-
'oauth'
|
|
37
|
-
)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
test.afterAll(() => {
|
|
41
|
-
stopServer(OAUTH_PORT)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test('/.well-known/oauth-protected-resource should return RFC 9728 metadata', async () => {
|
|
45
|
-
const response = await fetch(
|
|
46
|
-
`http://localhost:${OAUTH_PORT}/.well-known/oauth-protected-resource`
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
expect(response.status).toBe(200)
|
|
50
|
-
const body = await response.json()
|
|
51
|
-
|
|
52
|
-
// RFC 9728 required fields
|
|
53
|
-
expect(body).toHaveProperty('resource')
|
|
54
|
-
expect(body).toHaveProperty('authorization_servers')
|
|
55
|
-
expect(Array.isArray(body.authorization_servers)).toBe(true)
|
|
56
|
-
expect(body.authorization_servers.length).toBeGreaterThan(0)
|
|
57
|
-
|
|
58
|
-
// Should include the issuer we configured
|
|
59
|
-
expect(body.authorization_servers).toContain('https://auth.example.com/realms/test')
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
test('/.well-known/oauth-protected-resource should include scopes', async () => {
|
|
63
|
-
const response = await fetch(
|
|
64
|
-
`http://localhost:${OAUTH_PORT}/.well-known/oauth-protected-resource`
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
const body = await response.json()
|
|
68
|
-
expect(body).toHaveProperty('scopes_supported')
|
|
69
|
-
expect(Array.isArray(body.scopes_supported)).toBe(true)
|
|
70
|
-
|
|
71
|
-
// Should include the 3 scope levels
|
|
72
|
-
const scopes = body.scopes_supported as string[]
|
|
73
|
-
expect(scopes).toContain('read')
|
|
74
|
-
expect(scopes).toContain('write')
|
|
75
|
-
expect(scopes).toContain('admin')
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
test('MCP endpoints should require authentication with OAuth enabled', async () => {
|
|
79
|
-
// POST to /mcp without a token should be rejected
|
|
80
|
-
const response = await fetch(`http://localhost:${OAUTH_PORT}/mcp`, {
|
|
81
|
-
method: 'POST',
|
|
82
|
-
headers: {
|
|
83
|
-
'Content-Type': 'application/json',
|
|
84
|
-
Accept: 'application/json, text/event-stream',
|
|
85
|
-
},
|
|
86
|
-
body: JSON.stringify({
|
|
87
|
-
jsonrpc: '2.0',
|
|
88
|
-
id: 1,
|
|
89
|
-
method: 'initialize',
|
|
90
|
-
params: {
|
|
91
|
-
protocolVersion: '2025-03-26',
|
|
92
|
-
capabilities: {},
|
|
93
|
-
clientInfo: { name: 'oauth-test', version: '1.0' },
|
|
94
|
-
},
|
|
95
|
-
}),
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
// Should be 401 (unauthorized) since no valid JWT is provided
|
|
99
|
-
expect(response.status).toBe(401)
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
})
|