triflux 10.3.4 → 10.7.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/LICENSE +21 -21
- package/bin/tfx-doctor-tui.mjs +1 -1
- package/bin/tfx-doctor.mjs +6 -1
- package/bin/tfx-profile.mjs +1 -1
- package/bin/tfx-setup-tui.mjs +1 -1
- package/bin/tfx-setup.mjs +6 -1
- package/bin/triflux.mjs +2396 -1140
- package/hooks/agent-route-guard.mjs +12 -8
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +19 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +108 -24
- package/hooks/hook-registry.json +11 -0
- package/hooks/keyword-rules.json +6 -10
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/pipeline-stop.mjs +3 -6
- package/hooks/safety-guard.mjs +99 -28
- package/hooks/session-start-fast.mjs +143 -0
- package/hooks/subagent-verifier.mjs +5 -4
- package/hub/account-broker.mjs +256 -60
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +95 -57
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +67 -30
- package/hub/bridge.mjs +0 -1
- package/hub/cli-adapter-base.mjs +200 -48
- package/hub/codex-adapter.mjs +76 -96
- package/hub/codex-compat.mjs +3 -3
- package/hub/codex-preflight.mjs +63 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +3 -3
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +5 -5
- package/hub/fullcycle.mjs +33 -17
- package/hub/gemini-adapter.mjs +69 -94
- package/hub/hitl.mjs +89 -30
- package/hub/intent.mjs +161 -38
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/mcp-response-cache.mjs +66 -32
- package/hub/lib/memory-store.mjs +285 -111
- package/hub/lib/path-utils.mjs +35 -37
- package/hub/lib/process-utils.mjs +106 -37
- package/hub/lib/spawn-trace.mjs +527 -0
- package/hub/lib/ssh-command.mjs +34 -4
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/request-logger.mjs +61 -34
- package/hub/paths.mjs +9 -9
- package/hub/pipeline/gates/confidence.mjs +34 -15
- package/hub/pipeline/gates/consensus.mjs +27 -15
- package/hub/pipeline/gates/index.mjs +7 -3
- package/hub/pipeline/gates/selfcheck.mjs +57 -19
- package/hub/pipeline/index.mjs +77 -42
- package/hub/pipeline/state.mjs +10 -10
- package/hub/pipeline/transitions.mjs +40 -23
- package/hub/platform.mjs +57 -48
- package/hub/promote-penalties.mjs +25 -7
- package/hub/quality/deslop.mjs +70 -49
- package/hub/research.mjs +32 -25
- package/hub/router.mjs +240 -107
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/server.mjs +4 -4
- package/hub/session-fingerprint.mjs +126 -60
- package/hub/state.mjs +84 -43
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +22 -7
- package/hub/team/ansi.mjs +186 -122
- package/hub/team/backend.mjs +28 -10
- package/hub/team/cli/commands/attach.mjs +29 -9
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +32 -11
- package/hub/team/cli/commands/focus.mjs +38 -11
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +11 -4
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -31
- package/hub/team/cli/commands/start/parse-args.mjs +38 -11
- package/hub/team/cli/commands/start/start-headless.mjs +112 -36
- package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
- package/hub/team/cli/commands/start/start-mux.mjs +70 -21
- package/hub/team/cli/commands/start/start-wt.mjs +29 -12
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +1 -1
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +30 -8
- package/hub/team/cli/services/attach-fallback.mjs +31 -11
- package/hub/team/cli/services/hub-client.mjs +42 -14
- package/hub/team/cli/services/member-selector.mjs +11 -4
- package/hub/team/cli/services/native-control.mjs +48 -21
- package/hub/team/cli/services/runtime-mode.mjs +2 -1
- package/hub/team/cli/services/state-store.mjs +25 -8
- package/hub/team/cli/services/task-model.mjs +16 -6
- package/hub/team/conductor-mesh-bridge.mjs +24 -23
- package/hub/team/conductor.mjs +8 -4
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +41 -21
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/handoff.mjs +31 -15
- package/hub/team/headless.mjs +2 -1
- package/hub/team/health-probe.mjs +69 -54
- package/hub/team/launcher-template.mjs +16 -13
- package/hub/team/native-supervisor.mjs +65 -21
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +119 -76
- package/hub/team/orchestrator.mjs +9 -4
- package/hub/team/pane.mjs +12 -7
- package/hub/team/process-cleanup.mjs +25 -16
- package/hub/team/psmux.mjs +491 -201
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +117 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +3 -1
- package/hub/team/session.mjs +98 -34
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-locks.mjs +15 -13
- package/hub/team/swarm-planner.mjs +32 -21
- package/hub/team/swarm-reconciler.mjs +48 -23
- package/hub/team/tui-lite.mjs +266 -68
- package/hub/team/tui-remote-adapter.mjs +14 -10
- package/hub/team/tui-viewer.mjs +99 -43
- package/hub/team/tui.mjs +708 -271
- package/hub/team/worktree-lifecycle.mjs +152 -58
- package/hub/team/wt-manager.mjs +24 -14
- package/hub/token-mode.mjs +71 -71
- package/hub/tray.mjs +66 -23
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-mcp.mjs +192 -141
- package/hub/workers/delegator-mcp.mjs +507 -333
- package/hub/workers/factory.mjs +8 -8
- package/hub/workers/gemini-worker.mjs +115 -84
- package/hub/workers/interface.mjs +6 -1
- package/hub/workers/worker-utils.mjs +21 -14
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +162 -26
- package/hud/context-monitor.mjs +82 -41
- package/hud/hud-qos-status.mjs +129 -49
- package/hud/mission-board.mjs +6 -3
- package/hud/providers/claude.mjs +226 -115
- package/hud/providers/codex.mjs +62 -22
- package/hud/providers/gemini.mjs +168 -56
- package/hud/renderers.mjs +384 -119
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +78 -38
- package/mesh/index.mjs +11 -5
- package/mesh/mesh-budget.mjs +18 -9
- package/mesh/mesh-heartbeat.mjs +1 -1
- package/mesh/mesh-queue.mjs +3 -5
- package/mesh/mesh-router.mjs +5 -4
- package/package.json +2 -1
- package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
- package/scripts/__tests__/keyword-detector.test.mjs +77 -28
- package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
- package/scripts/__tests__/remote-spawn.test.mjs +10 -4
- package/scripts/__tests__/session-start-fast.test.mjs +36 -0
- package/scripts/__tests__/skill-template.test.mjs +98 -50
- package/scripts/__tests__/smoke.test.mjs +1 -1
- package/scripts/__tests__/spawn-trace.test.mjs +102 -0
- package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
- package/scripts/cache-doctor.mjs +11 -4
- package/scripts/cache-warmup.mjs +96 -37
- package/scripts/claudemd-sync.mjs +27 -17
- package/scripts/codex-gateway-preflight.mjs +52 -37
- package/scripts/codex-mcp-gateway-sync.mjs +59 -39
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/config-audit.mjs +232 -0
- package/scripts/convert-to-tmpl.mjs +54 -0
- package/scripts/cross-review-gate.mjs +35 -12
- package/scripts/cross-review-tracker.mjs +21 -8
- package/scripts/demo.mjs +35 -17
- package/scripts/doctor-diagnose.mjs +284 -0
- package/scripts/gen-skill-docs.mjs +7 -2
- package/scripts/gen-skill-manifest.mjs +2 -1
- package/scripts/headless-guard.mjs +86 -48
- package/scripts/hub-ensure.mjs +45 -26
- package/scripts/keyword-detector.mjs +41 -20
- package/scripts/keyword-rules-expander.mjs +47 -30
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +6 -3
- package/scripts/lib/env-probe.mjs +47 -28
- package/scripts/lib/gemini-profiles.mjs +44 -10
- package/scripts/lib/handoff.mjs +33 -17
- package/scripts/lib/hook-utils.mjs +8 -6
- package/scripts/lib/keyword-rules.mjs +43 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +377 -239
- package/scripts/lib/mcp-guard-engine.mjs +194 -79
- package/scripts/lib/mcp-manifest.mjs +23 -13
- package/scripts/lib/mcp-server-catalog.mjs +300 -63
- package/scripts/lib/psmux-info.mjs +11 -6
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-template.mjs +30 -7
- package/scripts/mcp-check.mjs +58 -39
- package/scripts/mcp-gateway-config.mjs +83 -39
- package/scripts/mcp-gateway-ensure.mjs +43 -35
- package/scripts/mcp-gateway-integration-test.mjs +70 -58
- package/scripts/mcp-gateway-start.mjs +126 -60
- package/scripts/mcp-gateway-verify.mjs +24 -22
- package/scripts/mcp-safety-guard.mjs +44 -11
- package/scripts/notion-read.mjs +199 -84
- package/scripts/pack.mjs +94 -89
- package/scripts/preflight-cache.mjs +27 -10
- package/scripts/preinstall.mjs +42 -13
- package/scripts/remote-spawn.mjs +309 -94
- package/scripts/run.cjs +8 -5
- package/scripts/session-spawn-helper.mjs +130 -39
- package/scripts/session-stale-cleanup.mjs +123 -0
- package/scripts/setup.mjs +941 -492
- package/scripts/test-lock.mjs +20 -7
- package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
- package/scripts/tfx-batch-stats.mjs +32 -11
- package/scripts/tfx-gate-activate.mjs +11 -4
- package/scripts/tfx-route-post.mjs +87 -20
- package/scripts/tfx-route-worker.mjs +57 -51
- package/scripts/tfx-route.sh +41 -124
- package/scripts/tmp-cleanup.mjs +21 -7
- package/scripts/token-snapshot.mjs +204 -85
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/_templates/base.md +1 -6
- package/skills/merge-worktree/SKILL.md.tmpl +144 -0
- package/skills/shared/telemetry-segment.md +6 -0
- package/skills/star-prompt/SKILL.md.tmpl +222 -0
- package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
- package/skills/tfx-analysis/skill.json +1 -6
- package/skills/tfx-auto/SKILL.md +1 -0
- package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
- package/skills/tfx-auto-codex/skill.json +1 -3
- package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
- package/skills/tfx-autopilot/skill.json +1 -5
- package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
- package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
- package/skills/tfx-autoroute/skill.json +1 -7
- package/skills/tfx-codex/SKILL.md +1 -0
- package/skills/tfx-codex/skill.json +1 -3
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
- package/skills/tfx-codex-swarm/evals/evals.json +1 -1
- package/skills/tfx-codex-swarm/skill.json +1 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
- package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
- package/skills/tfx-debate/SKILL.md.tmpl +192 -0
- package/skills/tfx-debate/skill.json +1 -7
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
- package/skills/tfx-deep-analysis/skill.json +1 -5
- package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
- package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
- package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
- package/skills/tfx-deep-qa/skill.json +1 -6
- package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
- package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
- package/skills/tfx-doctor/SKILL.md +21 -0
- package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
- package/skills/tfx-doctor/skill.json +1 -3
- package/skills/tfx-find/SKILL.md +1 -0
- package/skills/tfx-forge/SKILL.md.tmpl +187 -0
- package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
- package/skills/tfx-fullcycle/skill.json +1 -6
- package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
- package/skills/tfx-gemini/skill.json +1 -3
- package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
- package/skills/tfx-hooks/skill.json +1 -3
- package/skills/tfx-hub/SKILL.md.tmpl +212 -0
- package/skills/tfx-hub/skill.json +1 -3
- package/skills/tfx-index/SKILL.md +1 -0
- package/skills/tfx-index/skill.json +1 -6
- package/skills/tfx-interview/SKILL.md.tmpl +285 -0
- package/skills/tfx-multi/SKILL.md.tmpl +183 -0
- package/skills/tfx-multi/skill.json +1 -3
- package/skills/tfx-panel/SKILL.md.tmpl +189 -0
- package/skills/tfx-panel/skill.json +1 -7
- package/skills/tfx-persist/SKILL.md.tmpl +270 -0
- package/skills/tfx-persist/skill.json +1 -7
- package/skills/tfx-plan/SKILL.md +1 -0
- package/skills/tfx-plan/skill.json +1 -6
- package/skills/tfx-profile/SKILL.md.tmpl +239 -0
- package/skills/tfx-profile/skill.json +1 -3
- package/skills/tfx-prune/SKILL.md.tmpl +200 -0
- package/skills/tfx-prune/skill.json +1 -7
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
- package/skills/tfx-psmux-rules/skill.json +1 -4
- package/skills/tfx-qa/SKILL.md +1 -0
- package/skills/tfx-qa/skill.json +1 -6
- package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
- package/skills/tfx-ralph/skill.json +1 -4
- package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
- package/skills/tfx-remote-setup/skill.json +1 -3
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
- package/skills/tfx-remote-spawn/references/hosts.json +16 -0
- package/skills/tfx-remote-spawn/skill.json +1 -4
- package/skills/tfx-research/SKILL.md +1 -0
- package/skills/tfx-review/SKILL.md +1 -0
- package/skills/tfx-review/skill.json +1 -6
- package/skills/tfx-setup/SKILL.md.tmpl +504 -0
- package/skills/tfx-setup/skill.json +1 -3
- package/skills/tfx-swarm/SKILL.md +22 -0
- package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
- package/tui/codex-profile.mjs +88 -33
- package/tui/core.mjs +45 -15
- package/tui/doctor.mjs +75 -28
- package/tui/gemini-profile.mjs +74 -29
- package/tui/monitor-data.mjs +8 -4
- package/tui/monitor.mjs +71 -27
- package/tui/setup.mjs +133 -42
|
@@ -2,21 +2,22 @@
|
|
|
2
2
|
// scripts/lib/mcp-filter.mjs
|
|
3
3
|
// 역할/컨텍스트 기반 MCP 도구 노출 정책의 단일 소스.
|
|
4
4
|
|
|
5
|
-
import { readFileSync } from
|
|
6
|
-
import process from
|
|
7
|
-
import { fileURLToPath } from
|
|
8
|
-
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { CORE_SERVERS, readManifest } from "./mcp-manifest.mjs";
|
|
9
9
|
import {
|
|
10
10
|
DOMAIN_TAG_KEYWORDS,
|
|
11
11
|
MCP_SERVER_TOOL_CATALOG,
|
|
12
|
+
normalizeServerMetadata,
|
|
12
13
|
SEARCH_SERVER_ORDER,
|
|
13
14
|
SERVER_EXPLICIT_KEYWORDS,
|
|
14
|
-
normalizeServerMetadata,
|
|
15
15
|
uniqueStrings,
|
|
16
|
-
} from
|
|
17
|
-
import { readManifest, CORE_SERVERS } from './mcp-manifest.mjs';
|
|
16
|
+
} from "./mcp-server-catalog.mjs";
|
|
18
17
|
|
|
19
|
-
export const KNOWN_MCP_SERVERS = Object.freeze(
|
|
18
|
+
export const KNOWN_MCP_SERVERS = Object.freeze(
|
|
19
|
+
Object.keys(MCP_SERVER_TOOL_CATALOG),
|
|
20
|
+
);
|
|
20
21
|
|
|
21
22
|
const SEARCH_INTENT_PATTERNS = Object.freeze([
|
|
22
23
|
/\b(search|web|browse|look ?up|find|latest|recent|news|current|today|release(?: note)?s?|changelog|announcement|pricing|status|verify|fact[- ]?check)\b/i,
|
|
@@ -25,102 +26,129 @@ const SEARCH_INTENT_PATTERNS = Object.freeze([
|
|
|
25
26
|
|
|
26
27
|
const PROFILE_DEFINITIONS = Object.freeze({
|
|
27
28
|
default: Object.freeze({
|
|
28
|
-
description:
|
|
29
|
-
allowedServers: Object.freeze([
|
|
30
|
-
alwaysOnServers: Object.freeze([
|
|
29
|
+
description: "보수적 기본 프로필. 문서 조회 + 최소 검색만 허용",
|
|
30
|
+
allowedServers: Object.freeze(["context7", "brave-search"]),
|
|
31
|
+
alwaysOnServers: Object.freeze(["context7"]),
|
|
31
32
|
maxSearchServers: 1,
|
|
32
33
|
allowedToolsByServer: Object.freeze({
|
|
33
|
-
context7: Object.freeze([
|
|
34
|
-
|
|
34
|
+
context7: Object.freeze(["resolve-library-id", "query-docs"]),
|
|
35
|
+
"brave-search": Object.freeze(["brave_web_search", "brave_news_search"]),
|
|
35
36
|
}),
|
|
36
37
|
}),
|
|
37
38
|
executor: Object.freeze({
|
|
38
|
-
description:
|
|
39
|
-
allowedServers: Object.freeze([
|
|
40
|
-
|
|
39
|
+
description: "구현 워커용. 문서/검색/브라우징 보조 MCP 허용",
|
|
40
|
+
allowedServers: Object.freeze([
|
|
41
|
+
"context7",
|
|
42
|
+
"playwright",
|
|
43
|
+
"brave-search",
|
|
44
|
+
"tavily",
|
|
45
|
+
"exa",
|
|
46
|
+
]),
|
|
47
|
+
alwaysOnServers: Object.freeze(["context7"]),
|
|
41
48
|
maxSearchServers: 2,
|
|
42
49
|
allowedToolsByServer: Object.freeze({
|
|
43
|
-
context7: Object.freeze([
|
|
44
|
-
|
|
45
|
-
exa: Object.freeze([
|
|
46
|
-
tavily: Object.freeze([
|
|
50
|
+
context7: Object.freeze(["resolve-library-id", "query-docs"]),
|
|
51
|
+
"brave-search": Object.freeze(["brave_web_search", "brave_news_search"]),
|
|
52
|
+
exa: Object.freeze(["web_search_exa", "get_code_context_exa"]),
|
|
53
|
+
tavily: Object.freeze(["tavily_search", "tavily_extract"]),
|
|
47
54
|
playwright: Object.freeze([
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
"browser_navigate",
|
|
56
|
+
"browser_navigate_back",
|
|
57
|
+
"browser_snapshot",
|
|
58
|
+
"browser_take_screenshot",
|
|
59
|
+
"browser_wait_for",
|
|
53
60
|
]),
|
|
54
61
|
}),
|
|
55
62
|
}),
|
|
56
63
|
designer: Object.freeze({
|
|
57
|
-
description:
|
|
58
|
-
allowedServers: Object.freeze([
|
|
59
|
-
|
|
64
|
+
description: "디자인/UI 워커용. 브라우저 관찰 + 문서 조회 중심 MCP 허용",
|
|
65
|
+
allowedServers: Object.freeze([
|
|
66
|
+
"context7",
|
|
67
|
+
"playwright",
|
|
68
|
+
"tavily",
|
|
69
|
+
"exa",
|
|
70
|
+
"brave-search",
|
|
71
|
+
]),
|
|
72
|
+
alwaysOnServers: Object.freeze(["context7"]),
|
|
60
73
|
maxSearchServers: 2,
|
|
61
74
|
allowedToolsByServer: Object.freeze({
|
|
62
|
-
context7: Object.freeze([
|
|
63
|
-
|
|
64
|
-
exa: Object.freeze([
|
|
65
|
-
tavily: Object.freeze([
|
|
75
|
+
context7: Object.freeze(["resolve-library-id", "query-docs"]),
|
|
76
|
+
"brave-search": Object.freeze(["brave_web_search", "brave_news_search"]),
|
|
77
|
+
exa: Object.freeze(["web_search_exa", "get_code_context_exa"]),
|
|
78
|
+
tavily: Object.freeze(["tavily_search", "tavily_extract"]),
|
|
66
79
|
playwright: Object.freeze([
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
80
|
+
"browser_navigate",
|
|
81
|
+
"browser_navigate_back",
|
|
82
|
+
"browser_snapshot",
|
|
83
|
+
"browser_take_screenshot",
|
|
84
|
+
"browser_wait_for",
|
|
72
85
|
]),
|
|
73
86
|
}),
|
|
74
87
|
}),
|
|
75
88
|
analyze: Object.freeze({
|
|
76
|
-
description:
|
|
77
|
-
allowedServers: Object.freeze([
|
|
78
|
-
|
|
89
|
+
description: "분석/설계 워커용. 추론 + 검색 MCP 허용",
|
|
90
|
+
allowedServers: Object.freeze([
|
|
91
|
+
"context7",
|
|
92
|
+
"brave-search",
|
|
93
|
+
"tavily",
|
|
94
|
+
"exa",
|
|
95
|
+
"sequential-thinking",
|
|
96
|
+
]),
|
|
97
|
+
alwaysOnServers: Object.freeze(["context7", "sequential-thinking"]),
|
|
79
98
|
maxSearchServers: 2,
|
|
80
99
|
allowedToolsByServer: Object.freeze({
|
|
81
|
-
context7: Object.freeze([
|
|
82
|
-
|
|
83
|
-
exa: Object.freeze([
|
|
84
|
-
tavily: Object.freeze([
|
|
85
|
-
|
|
100
|
+
context7: Object.freeze(["resolve-library-id", "query-docs"]),
|
|
101
|
+
"brave-search": Object.freeze(["brave_web_search", "brave_news_search"]),
|
|
102
|
+
exa: Object.freeze(["web_search_exa", "get_code_context_exa"]),
|
|
103
|
+
tavily: Object.freeze(["tavily_search", "tavily_extract"]),
|
|
104
|
+
"sequential-thinking": Object.freeze(["sequentialthinking"]),
|
|
86
105
|
}),
|
|
87
106
|
}),
|
|
88
107
|
explore: Object.freeze({
|
|
89
|
-
description:
|
|
90
|
-
allowedServers: Object.freeze([
|
|
91
|
-
|
|
108
|
+
description: "탐색/리서치 워커용. 읽기/검색 중심 MCP만 허용",
|
|
109
|
+
allowedServers: Object.freeze([
|
|
110
|
+
"context7",
|
|
111
|
+
"brave-search",
|
|
112
|
+
"tavily",
|
|
113
|
+
"exa",
|
|
114
|
+
]),
|
|
115
|
+
alwaysOnServers: Object.freeze(["context7"]),
|
|
92
116
|
maxSearchServers: 2,
|
|
93
117
|
allowedToolsByServer: Object.freeze({
|
|
94
|
-
context7: Object.freeze([
|
|
95
|
-
|
|
96
|
-
exa: Object.freeze([
|
|
97
|
-
tavily: Object.freeze([
|
|
118
|
+
context7: Object.freeze(["resolve-library-id", "query-docs"]),
|
|
119
|
+
"brave-search": Object.freeze(["brave_web_search", "brave_news_search"]),
|
|
120
|
+
exa: Object.freeze(["web_search_exa", "get_code_context_exa"]),
|
|
121
|
+
tavily: Object.freeze(["tavily_search", "tavily_extract"]),
|
|
98
122
|
}),
|
|
99
123
|
}),
|
|
100
124
|
reviewer: Object.freeze({
|
|
101
|
-
description:
|
|
102
|
-
allowedServers: Object.freeze([
|
|
103
|
-
|
|
125
|
+
description: "리뷰 워커용. 문서 조회 + 분석 전용 MCP만 허용",
|
|
126
|
+
allowedServers: Object.freeze([
|
|
127
|
+
"context7",
|
|
128
|
+
"brave-search",
|
|
129
|
+
"sequential-thinking",
|
|
130
|
+
]),
|
|
131
|
+
alwaysOnServers: Object.freeze(["context7", "sequential-thinking"]),
|
|
104
132
|
maxSearchServers: 1,
|
|
105
133
|
allowedToolsByServer: Object.freeze({
|
|
106
|
-
context7: Object.freeze([
|
|
107
|
-
|
|
108
|
-
|
|
134
|
+
context7: Object.freeze(["resolve-library-id", "query-docs"]),
|
|
135
|
+
"brave-search": Object.freeze(["brave_web_search"]),
|
|
136
|
+
"sequential-thinking": Object.freeze(["sequentialthinking"]),
|
|
109
137
|
}),
|
|
110
138
|
}),
|
|
111
139
|
writer: Object.freeze({
|
|
112
|
-
description:
|
|
113
|
-
allowedServers: Object.freeze([
|
|
114
|
-
alwaysOnServers: Object.freeze([
|
|
140
|
+
description: "문서/작성 워커용. 공식 문서와 최소 검색 MCP만 허용",
|
|
141
|
+
allowedServers: Object.freeze(["context7", "brave-search", "exa"]),
|
|
142
|
+
alwaysOnServers: Object.freeze(["context7"]),
|
|
115
143
|
maxSearchServers: 2,
|
|
116
144
|
allowedToolsByServer: Object.freeze({
|
|
117
|
-
context7: Object.freeze([
|
|
118
|
-
|
|
119
|
-
exa: Object.freeze([
|
|
145
|
+
context7: Object.freeze(["resolve-library-id", "query-docs"]),
|
|
146
|
+
"brave-search": Object.freeze(["brave_web_search", "brave_news_search"]),
|
|
147
|
+
exa: Object.freeze(["web_search_exa"]),
|
|
120
148
|
}),
|
|
121
149
|
}),
|
|
122
150
|
none: Object.freeze({
|
|
123
|
-
description:
|
|
151
|
+
description: "모든 선택적 MCP 서버 비활성화",
|
|
124
152
|
allowedServers: Object.freeze([]),
|
|
125
153
|
alwaysOnServers: Object.freeze([]),
|
|
126
154
|
maxSearchServers: 0,
|
|
@@ -134,110 +162,111 @@ const PROFILE_DEFINITIONS = Object.freeze({
|
|
|
134
162
|
*/
|
|
135
163
|
export const PHASE_OVERRIDES = Object.freeze({
|
|
136
164
|
plan: Object.freeze({
|
|
137
|
-
description:
|
|
138
|
-
allowedServers: Object.freeze([
|
|
139
|
-
blockedServers: Object.freeze([
|
|
165
|
+
description: "계획 단계: 읽기 전용 탐색만 허용",
|
|
166
|
+
allowedServers: Object.freeze(["context7"]),
|
|
167
|
+
blockedServers: Object.freeze(["playwright", "tavily", "exa"]),
|
|
140
168
|
}),
|
|
141
169
|
prd: Object.freeze({
|
|
142
|
-
description:
|
|
143
|
-
allowedServers: Object.freeze([
|
|
144
|
-
blockedServers: Object.freeze([
|
|
170
|
+
description: "PRD 단계: 읽기 전용 탐색 + 문서 조회",
|
|
171
|
+
allowedServers: Object.freeze(["context7", "brave-search"]),
|
|
172
|
+
blockedServers: Object.freeze(["playwright"]),
|
|
145
173
|
}),
|
|
146
174
|
exec: Object.freeze({
|
|
147
|
-
description:
|
|
175
|
+
description: "실행 단계: 프로필 기반 전체 허용 (제한 없음)",
|
|
148
176
|
}),
|
|
149
177
|
verify: Object.freeze({
|
|
150
|
-
description:
|
|
151
|
-
allowedServers: Object.freeze([
|
|
152
|
-
blockedServers: Object.freeze([
|
|
178
|
+
description: "검증 단계: 읽기 전용 + 분석 도구",
|
|
179
|
+
allowedServers: Object.freeze(["context7", "brave-search", "exa"]),
|
|
180
|
+
blockedServers: Object.freeze(["playwright"]),
|
|
153
181
|
}),
|
|
154
182
|
});
|
|
155
183
|
|
|
156
184
|
export const LEGACY_PROFILE_ALIASES = Object.freeze({
|
|
157
|
-
implement:
|
|
158
|
-
analyze:
|
|
159
|
-
review:
|
|
160
|
-
docs:
|
|
161
|
-
minimal:
|
|
185
|
+
implement: "executor",
|
|
186
|
+
analyze: "analyze",
|
|
187
|
+
review: "reviewer",
|
|
188
|
+
docs: "writer",
|
|
189
|
+
minimal: "default",
|
|
162
190
|
});
|
|
163
191
|
|
|
164
192
|
export const SUPPORTED_MCP_PROFILES = Object.freeze([
|
|
165
|
-
|
|
193
|
+
"auto",
|
|
166
194
|
...Object.keys(PROFILE_DEFINITIONS),
|
|
167
195
|
...Object.keys(LEGACY_PROFILE_ALIASES),
|
|
168
196
|
]);
|
|
169
197
|
|
|
170
|
-
function normalizeTaskText(taskText =
|
|
171
|
-
if (typeof taskText !==
|
|
172
|
-
return taskText.replace(/\s+/g,
|
|
198
|
+
function normalizeTaskText(taskText = "") {
|
|
199
|
+
if (typeof taskText !== "string") return "";
|
|
200
|
+
return taskText.replace(/\s+/g, " ").trim();
|
|
173
201
|
}
|
|
174
202
|
|
|
175
203
|
function normalizeProfileName(profile) {
|
|
176
|
-
const raw =
|
|
177
|
-
|
|
204
|
+
const raw =
|
|
205
|
+
typeof profile === "string" && profile.trim() ? profile.trim() : "auto";
|
|
206
|
+
if (raw === "auto") return raw;
|
|
178
207
|
if (PROFILE_DEFINITIONS[raw]) return raw;
|
|
179
208
|
if (LEGACY_PROFILE_ALIASES[raw]) return LEGACY_PROFILE_ALIASES[raw];
|
|
180
209
|
// graceful fallback: --flag나 잘못된 프로필 → 'auto'로 폴백 (hard crash 방지)
|
|
181
|
-
if (raw.startsWith(
|
|
210
|
+
if (raw.startsWith("-") || raw.startsWith("/")) return "auto";
|
|
182
211
|
console.error(`[mcp-filter] 경고: 알 수 없는 프로필 '${raw}', 'auto'로 폴백`);
|
|
183
|
-
return
|
|
212
|
+
return "auto";
|
|
184
213
|
}
|
|
185
214
|
|
|
186
|
-
function resolveAutoProfile(agentType =
|
|
215
|
+
function resolveAutoProfile(agentType = "") {
|
|
187
216
|
switch (agentType) {
|
|
188
|
-
case
|
|
189
|
-
case
|
|
190
|
-
case
|
|
191
|
-
case
|
|
192
|
-
return
|
|
193
|
-
case
|
|
194
|
-
case
|
|
195
|
-
return
|
|
196
|
-
case
|
|
197
|
-
return
|
|
198
|
-
case
|
|
199
|
-
case
|
|
200
|
-
case
|
|
201
|
-
case
|
|
202
|
-
return
|
|
203
|
-
case
|
|
204
|
-
case
|
|
205
|
-
case
|
|
206
|
-
case
|
|
207
|
-
return
|
|
208
|
-
case
|
|
209
|
-
case
|
|
210
|
-
case
|
|
211
|
-
case
|
|
212
|
-
return
|
|
213
|
-
case
|
|
214
|
-
return
|
|
217
|
+
case "executor":
|
|
218
|
+
case "build-fixer":
|
|
219
|
+
case "debugger":
|
|
220
|
+
case "deep-executor":
|
|
221
|
+
return "executor";
|
|
222
|
+
case "test-engineer":
|
|
223
|
+
case "qa-tester":
|
|
224
|
+
return "none";
|
|
225
|
+
case "designer":
|
|
226
|
+
return "designer";
|
|
227
|
+
case "architect":
|
|
228
|
+
case "planner":
|
|
229
|
+
case "critic":
|
|
230
|
+
case "analyst":
|
|
231
|
+
return "analyze";
|
|
232
|
+
case "scientist":
|
|
233
|
+
case "scientist-deep":
|
|
234
|
+
case "document-specialist":
|
|
235
|
+
case "explore":
|
|
236
|
+
return "explore";
|
|
237
|
+
case "code-reviewer":
|
|
238
|
+
case "security-reviewer":
|
|
239
|
+
case "quality-reviewer":
|
|
240
|
+
case "verifier":
|
|
241
|
+
return "reviewer";
|
|
242
|
+
case "writer":
|
|
243
|
+
return "writer";
|
|
215
244
|
default:
|
|
216
|
-
return
|
|
245
|
+
return "default";
|
|
217
246
|
}
|
|
218
247
|
}
|
|
219
248
|
|
|
220
249
|
function escapeRegExp(value) {
|
|
221
|
-
return String(value).replace(/[.*+?^${}()|[\]\\]/g,
|
|
250
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
222
251
|
}
|
|
223
252
|
|
|
224
253
|
function countKeywordMatches(text, keywords = []) {
|
|
225
254
|
let matches = 0;
|
|
226
255
|
for (const keyword of keywords) {
|
|
227
|
-
const source = String(keyword ||
|
|
256
|
+
const source = String(keyword || "").trim();
|
|
228
257
|
if (!source) continue;
|
|
229
258
|
const pattern = /^[a-z0-9- ]+$/i.test(source)
|
|
230
|
-
? new RegExp(`\\b${escapeRegExp(source)}\\b`,
|
|
231
|
-
: new RegExp(escapeRegExp(source),
|
|
259
|
+
? new RegExp(`\\b${escapeRegExp(source)}\\b`, "i")
|
|
260
|
+
: new RegExp(escapeRegExp(source), "iu");
|
|
232
261
|
if (pattern.test(text)) matches += 1;
|
|
233
262
|
}
|
|
234
263
|
return matches;
|
|
235
264
|
}
|
|
236
265
|
|
|
237
|
-
function loadInventory(inventoryFile =
|
|
238
|
-
if (typeof inventoryFile !==
|
|
266
|
+
function loadInventory(inventoryFile = "") {
|
|
267
|
+
if (typeof inventoryFile !== "string" || !inventoryFile.trim()) return null;
|
|
239
268
|
try {
|
|
240
|
-
return JSON.parse(readFileSync(inventoryFile,
|
|
269
|
+
return JSON.parse(readFileSync(inventoryFile, "utf8"));
|
|
241
270
|
} catch {
|
|
242
271
|
return null;
|
|
243
272
|
}
|
|
@@ -245,12 +274,15 @@ function loadInventory(inventoryFile = '') {
|
|
|
245
274
|
|
|
246
275
|
function buildInventoryIndex(inventory = null) {
|
|
247
276
|
const index = new Map();
|
|
248
|
-
if (!inventory || typeof inventory !==
|
|
277
|
+
if (!inventory || typeof inventory !== "object") return index;
|
|
249
278
|
|
|
250
|
-
for (const client of [
|
|
251
|
-
const servers = Array.isArray(inventory[client]?.servers)
|
|
279
|
+
for (const client of ["codex", "gemini"]) {
|
|
280
|
+
const servers = Array.isArray(inventory[client]?.servers)
|
|
281
|
+
? inventory[client].servers
|
|
282
|
+
: [];
|
|
252
283
|
for (const server of servers) {
|
|
253
|
-
if (!server || typeof server.name !==
|
|
284
|
+
if (!server || typeof server.name !== "string" || !server.name.trim())
|
|
285
|
+
continue;
|
|
254
286
|
const name = server.name.trim();
|
|
255
287
|
const previous = index.get(name) || {};
|
|
256
288
|
index.set(name, {
|
|
@@ -278,7 +310,7 @@ function getServerMetadata(server, inventoryIndex) {
|
|
|
278
310
|
});
|
|
279
311
|
}
|
|
280
312
|
|
|
281
|
-
function scoreServer(server, taskText =
|
|
313
|
+
function scoreServer(server, taskText = "", inventoryIndex = new Map()) {
|
|
282
314
|
const normalized = normalizeTaskText(taskText);
|
|
283
315
|
const metadata = getServerMetadata(server, inventoryIndex);
|
|
284
316
|
if (!normalized) {
|
|
@@ -294,14 +326,20 @@ function scoreServer(server, taskText = '', inventoryIndex = new Map()) {
|
|
|
294
326
|
let score = 0;
|
|
295
327
|
const matchedTags = [];
|
|
296
328
|
for (const tag of metadata.domain_tags) {
|
|
297
|
-
const matches = countKeywordMatches(
|
|
329
|
+
const matches = countKeywordMatches(
|
|
330
|
+
normalized,
|
|
331
|
+
DOMAIN_TAG_KEYWORDS[tag] || [],
|
|
332
|
+
);
|
|
298
333
|
if (matches > 0) {
|
|
299
334
|
matchedTags.push(tag);
|
|
300
335
|
score += matches * 2;
|
|
301
336
|
}
|
|
302
337
|
}
|
|
303
338
|
|
|
304
|
-
const explicitMatches = countKeywordMatches(
|
|
339
|
+
const explicitMatches = countKeywordMatches(
|
|
340
|
+
normalized,
|
|
341
|
+
SERVER_EXPLICIT_KEYWORDS[server] || [],
|
|
342
|
+
);
|
|
305
343
|
if (explicitMatches > 0) {
|
|
306
344
|
score += explicitMatches * 4;
|
|
307
345
|
}
|
|
@@ -325,61 +363,86 @@ function compareRankedServers(left, right, workerIndex, availableOrder = []) {
|
|
|
325
363
|
return Number(right.explicitMatch) - Number(left.explicitMatch);
|
|
326
364
|
}
|
|
327
365
|
if (right.score !== left.score) return right.score - left.score;
|
|
328
|
-
if (left.toolCount !== right.toolCount)
|
|
329
|
-
|
|
330
|
-
|
|
366
|
+
if (left.toolCount !== right.toolCount)
|
|
367
|
+
return left.toolCount - right.toolCount;
|
|
368
|
+
|
|
369
|
+
if (
|
|
370
|
+
Number.isInteger(workerIndex) &&
|
|
371
|
+
workerIndex > 0 &&
|
|
372
|
+
availableOrder.length > 1
|
|
373
|
+
) {
|
|
331
374
|
const offset = (workerIndex - 1) % availableOrder.length;
|
|
332
|
-
const rotated = availableOrder
|
|
375
|
+
const rotated = availableOrder
|
|
376
|
+
.slice(offset)
|
|
377
|
+
.concat(availableOrder.slice(0, offset));
|
|
333
378
|
return rotated.indexOf(left.server) - rotated.indexOf(right.server);
|
|
334
379
|
}
|
|
335
380
|
|
|
336
|
-
return
|
|
381
|
+
return (
|
|
382
|
+
availableOrder.indexOf(left.server) - availableOrder.indexOf(right.server)
|
|
383
|
+
);
|
|
337
384
|
}
|
|
338
385
|
|
|
339
386
|
function rankServers(servers = [], options = {}) {
|
|
340
|
-
const inventoryIndex =
|
|
341
|
-
|
|
342
|
-
|
|
387
|
+
const inventoryIndex =
|
|
388
|
+
options.inventoryIndex instanceof Map
|
|
389
|
+
? options.inventoryIndex
|
|
390
|
+
: buildInventoryIndex(options.inventory);
|
|
343
391
|
return servers
|
|
344
392
|
.map((server) => scoreServer(server, options.taskText, inventoryIndex))
|
|
345
|
-
.sort((left, right) =>
|
|
393
|
+
.sort((left, right) =>
|
|
394
|
+
compareRankedServers(left, right, options.workerIndex, servers),
|
|
395
|
+
);
|
|
346
396
|
}
|
|
347
397
|
|
|
348
398
|
function hasContextSignals(servers = [], options = {}) {
|
|
349
399
|
return rankServers(servers, options).some((server) => server.score > 0);
|
|
350
400
|
}
|
|
351
401
|
|
|
352
|
-
function inferPreferredSearchTool(
|
|
402
|
+
function inferPreferredSearchTool(
|
|
403
|
+
taskText = "",
|
|
404
|
+
inventoryIndex = new Map(),
|
|
405
|
+
allowedServers = SEARCH_SERVER_ORDER,
|
|
406
|
+
) {
|
|
353
407
|
const ranked = rankServers(
|
|
354
408
|
SEARCH_SERVER_ORDER.filter((server) => allowedServers.includes(server)),
|
|
355
409
|
{ taskText, inventoryIndex },
|
|
356
410
|
);
|
|
357
|
-
return ranked.find((server) => server.score > 0)?.server ||
|
|
411
|
+
return ranked.find((server) => server.score > 0)?.server || "";
|
|
358
412
|
}
|
|
359
413
|
|
|
360
414
|
function selectContextualServers(baseServers, profile, options = {}) {
|
|
361
415
|
const taskText = normalizeTaskText(options.taskText);
|
|
362
416
|
if (!taskText || !baseServers.length) return [...baseServers];
|
|
363
417
|
|
|
364
|
-
const inventoryIndex =
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
418
|
+
const inventoryIndex =
|
|
419
|
+
options.inventoryIndex instanceof Map
|
|
420
|
+
? options.inventoryIndex
|
|
421
|
+
: buildInventoryIndex(options.inventory);
|
|
422
|
+
if (!hasContextSignals(baseServers, { ...options, inventoryIndex }))
|
|
423
|
+
return [...baseServers];
|
|
368
424
|
|
|
369
425
|
const selected = new Set(
|
|
370
|
-
(profile.alwaysOnServers || []).filter((server) =>
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
baseServers.includes('playwright')
|
|
374
|
-
&& /(?:browser|screenshot|layout|responsive|visual|screen|page|ui|ux|regression|캡처|스크린샷|레이아웃|반응형|화면|브라우저)/iu.test(taskText)
|
|
426
|
+
(profile.alwaysOnServers || []).filter((server) =>
|
|
427
|
+
baseServers.includes(server),
|
|
428
|
+
),
|
|
375
429
|
);
|
|
430
|
+
const wantsBrowserObservation =
|
|
431
|
+
baseServers.includes("playwright") &&
|
|
432
|
+
/(?:browser|screenshot|layout|responsive|visual|screen|page|ui|ux|regression|캡처|스크린샷|레이아웃|반응형|화면|브라우저)/iu.test(
|
|
433
|
+
taskText,
|
|
434
|
+
);
|
|
376
435
|
if (wantsBrowserObservation) {
|
|
377
|
-
selected.add(
|
|
436
|
+
selected.add("playwright");
|
|
378
437
|
}
|
|
379
|
-
const requestedSearchTool =
|
|
438
|
+
const requestedSearchTool =
|
|
439
|
+
typeof options.searchTool === "string" ? options.searchTool : "";
|
|
380
440
|
|
|
381
441
|
const rankedServers = rankServers(
|
|
382
|
-
baseServers.filter(
|
|
442
|
+
baseServers.filter(
|
|
443
|
+
(server) =>
|
|
444
|
+
!SEARCH_SERVER_ORDER.includes(server) && !selected.has(server),
|
|
445
|
+
),
|
|
383
446
|
{ ...options, inventoryIndex },
|
|
384
447
|
);
|
|
385
448
|
for (const ranked of rankedServers) {
|
|
@@ -399,11 +462,13 @@ function selectContextualServers(baseServers, profile, options = {}) {
|
|
|
399
462
|
taskText,
|
|
400
463
|
{ inventoryIndex },
|
|
401
464
|
);
|
|
402
|
-
const rankedSearchSet = new Set(
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
465
|
+
const rankedSearchSet = new Set(
|
|
466
|
+
orderedSearchServers.filter((server) => {
|
|
467
|
+
if (requestedSearchTool === server) return true;
|
|
468
|
+
const ranked = scoreServer(server, taskText, inventoryIndex);
|
|
469
|
+
return ranked.score > 0 || ranked.explicitMatch;
|
|
470
|
+
}),
|
|
471
|
+
);
|
|
407
472
|
const maxSearchServers = Number.isInteger(profile.maxSearchServers)
|
|
408
473
|
? profile.maxSearchServers
|
|
409
474
|
: orderedSearchServers.length;
|
|
@@ -419,12 +484,22 @@ function selectContextualServers(baseServers, profile, options = {}) {
|
|
|
419
484
|
selected.add(server);
|
|
420
485
|
}
|
|
421
486
|
|
|
422
|
-
const alwaysOnServers = baseServers.filter(
|
|
487
|
+
const alwaysOnServers = baseServers.filter(
|
|
488
|
+
(server) =>
|
|
489
|
+
selected.has(server) && (profile.alwaysOnServers || []).includes(server),
|
|
490
|
+
);
|
|
423
491
|
const contextualNonSearch = rankServers(
|
|
424
|
-
baseServers.filter(
|
|
492
|
+
baseServers.filter(
|
|
493
|
+
(server) =>
|
|
494
|
+
selected.has(server) &&
|
|
495
|
+
!SEARCH_SERVER_ORDER.includes(server) &&
|
|
496
|
+
!alwaysOnServers.includes(server),
|
|
497
|
+
),
|
|
425
498
|
{ ...options, inventoryIndex },
|
|
426
499
|
).map((entry) => entry.server);
|
|
427
|
-
const contextualSearch = orderedSearchServers.filter((server) =>
|
|
500
|
+
const contextualSearch = orderedSearchServers.filter((server) =>
|
|
501
|
+
selected.has(server),
|
|
502
|
+
);
|
|
428
503
|
const contextualServers = uniqueStrings([
|
|
429
504
|
...alwaysOnServers,
|
|
430
505
|
...contextualNonSearch,
|
|
@@ -433,34 +508,53 @@ function selectContextualServers(baseServers, profile, options = {}) {
|
|
|
433
508
|
return contextualServers.length ? contextualServers : [...baseServers];
|
|
434
509
|
}
|
|
435
510
|
|
|
436
|
-
export function resolveMcpProfile(agentType =
|
|
511
|
+
export function resolveMcpProfile(agentType = "", requestedProfile = "auto") {
|
|
437
512
|
const normalized = normalizeProfileName(requestedProfile);
|
|
438
|
-
return normalized ===
|
|
513
|
+
return normalized === "auto" ? resolveAutoProfile(agentType) : normalized;
|
|
439
514
|
}
|
|
440
515
|
|
|
441
|
-
export function parseAvailableServers(rawAvailableServers =
|
|
442
|
-
if (Array.isArray(rawAvailableServers))
|
|
443
|
-
|
|
516
|
+
export function parseAvailableServers(rawAvailableServers = "") {
|
|
517
|
+
if (Array.isArray(rawAvailableServers))
|
|
518
|
+
return uniqueStrings(rawAvailableServers);
|
|
519
|
+
if (typeof rawAvailableServers !== "string" || !rawAvailableServers.trim())
|
|
520
|
+
return [];
|
|
444
521
|
return uniqueStrings(rawAvailableServers.split(/[,\s]+/));
|
|
445
522
|
}
|
|
446
523
|
|
|
447
|
-
export function resolveSearchToolOrder(
|
|
448
|
-
|
|
524
|
+
export function resolveSearchToolOrder(
|
|
525
|
+
searchTool = "",
|
|
526
|
+
workerIndex,
|
|
527
|
+
allowedServers = SEARCH_SERVER_ORDER,
|
|
528
|
+
taskText = "",
|
|
529
|
+
options = {},
|
|
530
|
+
) {
|
|
531
|
+
const available = SEARCH_SERVER_ORDER.filter((tool) =>
|
|
532
|
+
allowedServers.includes(tool),
|
|
533
|
+
);
|
|
449
534
|
if (!available.length) return [];
|
|
450
535
|
|
|
451
|
-
const inventoryIndex =
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
536
|
+
const inventoryIndex =
|
|
537
|
+
options.inventoryIndex instanceof Map
|
|
538
|
+
? options.inventoryIndex
|
|
539
|
+
: buildInventoryIndex(options.inventory);
|
|
540
|
+
const preferredSearchTool =
|
|
541
|
+
searchTool && available.includes(searchTool)
|
|
542
|
+
? searchTool
|
|
543
|
+
: inferPreferredSearchTool(taskText, inventoryIndex, available);
|
|
457
544
|
|
|
458
|
-
const ranked = rankServers(available, {
|
|
545
|
+
const ranked = rankServers(available, {
|
|
546
|
+
taskText,
|
|
547
|
+
workerIndex,
|
|
548
|
+
inventoryIndex,
|
|
549
|
+
}).map((entry) => entry.server);
|
|
459
550
|
if (!preferredSearchTool || !available.includes(preferredSearchTool)) {
|
|
460
551
|
return ranked;
|
|
461
552
|
}
|
|
462
553
|
|
|
463
|
-
return [
|
|
554
|
+
return [
|
|
555
|
+
preferredSearchTool,
|
|
556
|
+
...ranked.filter((tool) => tool !== preferredSearchTool),
|
|
557
|
+
];
|
|
464
558
|
}
|
|
465
559
|
|
|
466
560
|
function getProfileDefinition(resolvedProfile) {
|
|
@@ -476,27 +570,43 @@ function applyManifestFilter(servers) {
|
|
|
476
570
|
}
|
|
477
571
|
|
|
478
572
|
export function resolveAllowedServers(options = {}) {
|
|
479
|
-
const resolvedProfile = resolveMcpProfile(
|
|
573
|
+
const resolvedProfile = resolveMcpProfile(
|
|
574
|
+
options.agentType,
|
|
575
|
+
options.requestedProfile,
|
|
576
|
+
);
|
|
480
577
|
const profile = getProfileDefinition(resolvedProfile);
|
|
481
578
|
const availableServers = parseAvailableServers(options.availableServers);
|
|
482
579
|
const inventory = options.inventory || loadInventory(options.inventoryFile);
|
|
483
580
|
const inventoryIndex = buildInventoryIndex(inventory);
|
|
484
581
|
const baseServers = availableServers.length
|
|
485
|
-
? profile.allowedServers.filter((server) =>
|
|
582
|
+
? profile.allowedServers.filter((server) =>
|
|
583
|
+
availableServers.includes(server),
|
|
584
|
+
)
|
|
486
585
|
: [...profile.allowedServers];
|
|
487
586
|
const manifestFiltered = availableServers.length
|
|
488
587
|
? baseServers
|
|
489
588
|
: applyManifestFilter(baseServers);
|
|
490
|
-
return selectContextualServers(manifestFiltered, profile, {
|
|
589
|
+
return selectContextualServers(manifestFiltered, profile, {
|
|
590
|
+
...options,
|
|
591
|
+
inventory,
|
|
592
|
+
inventoryIndex,
|
|
593
|
+
});
|
|
491
594
|
}
|
|
492
595
|
|
|
493
596
|
export function buildPromptHint(options = {}) {
|
|
494
|
-
const resolvedProfile = resolveMcpProfile(
|
|
495
|
-
|
|
597
|
+
const resolvedProfile = resolveMcpProfile(
|
|
598
|
+
options.agentType,
|
|
599
|
+
options.requestedProfile,
|
|
600
|
+
);
|
|
601
|
+
if (resolvedProfile === "none") return "";
|
|
496
602
|
|
|
497
603
|
const inventory = options.inventory || loadInventory(options.inventoryFile);
|
|
498
604
|
const inventoryIndex = buildInventoryIndex(inventory);
|
|
499
|
-
const allowedServers = resolveAllowedServers({
|
|
605
|
+
const allowedServers = resolveAllowedServers({
|
|
606
|
+
...options,
|
|
607
|
+
inventory,
|
|
608
|
+
inventoryIndex,
|
|
609
|
+
});
|
|
500
610
|
const orderedTools = resolveSearchToolOrder(
|
|
501
611
|
options.searchTool,
|
|
502
612
|
Number.isInteger(options.workerIndex) ? options.workerIndex : undefined,
|
|
@@ -505,28 +615,40 @@ export function buildPromptHint(options = {}) {
|
|
|
505
615
|
{ inventory, inventoryIndex },
|
|
506
616
|
);
|
|
507
617
|
const has = (server) => allowedServers.includes(server);
|
|
508
|
-
const orderedSearchHint =
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
618
|
+
const orderedSearchHint =
|
|
619
|
+
orderedTools.length > 1
|
|
620
|
+
? `웹 검색 우선순위: ${orderedTools.join(", ")}.`
|
|
621
|
+
: orderedTools[0]
|
|
622
|
+
? `웹 검색은 ${orderedTools[0]}를 사용하세요.`
|
|
623
|
+
: "";
|
|
624
|
+
const searchFallbackHint =
|
|
625
|
+
orderedTools.length > 1
|
|
626
|
+
? "검색 도구 실패 시 402, 429, 432, 433, quota 에러에서 재시도하지 말고 다음 도구로 전환하세요."
|
|
627
|
+
: "";
|
|
516
628
|
return [
|
|
517
|
-
has(
|
|
518
|
-
has(
|
|
519
|
-
? resolvedProfile ===
|
|
520
|
-
?
|
|
521
|
-
:
|
|
522
|
-
:
|
|
523
|
-
has(
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
resolvedProfile
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
629
|
+
has("context7") ? "context7으로 관련 문서를 조회하세요." : "",
|
|
630
|
+
has("playwright")
|
|
631
|
+
? resolvedProfile === "designer"
|
|
632
|
+
? "화면/레이아웃 확인은 playwright를 우선 사용하세요."
|
|
633
|
+
: "브라우저/UI 검증이 필요하면 playwright를 사용하세요."
|
|
634
|
+
: "",
|
|
635
|
+
has("sequential-thinking")
|
|
636
|
+
? "sequential-thinking으로 체계적으로 분석하세요."
|
|
637
|
+
: "",
|
|
638
|
+
resolvedProfile === "reviewer" && orderedTools[0]
|
|
639
|
+
? `외부 근거가 더 필요하면 ${orderedTools[0]}를 사용하세요.`
|
|
640
|
+
: "",
|
|
641
|
+
resolvedProfile !== "reviewer" ? orderedSearchHint : "",
|
|
642
|
+
resolvedProfile !== "reviewer" ? searchFallbackHint : "",
|
|
643
|
+
resolvedProfile === "explore"
|
|
644
|
+
? "검색 깊이를 제한하고 읽기 전용 조사에 집중하세요."
|
|
645
|
+
: "",
|
|
646
|
+
resolvedProfile === "writer"
|
|
647
|
+
? "검색 결과의 출처 URL을 함께 제시하세요."
|
|
648
|
+
: "",
|
|
649
|
+
]
|
|
650
|
+
.filter(Boolean)
|
|
651
|
+
.join(" ");
|
|
530
652
|
}
|
|
531
653
|
|
|
532
654
|
export function getGeminiAllowedServers(options = {}) {
|
|
@@ -535,7 +657,10 @@ export function getGeminiAllowedServers(options = {}) {
|
|
|
535
657
|
|
|
536
658
|
export function getCodexMcpConfig(options = {}) {
|
|
537
659
|
const allowedServers = new Set(resolveAllowedServers(options));
|
|
538
|
-
const resolvedProfile = resolveMcpProfile(
|
|
660
|
+
const resolvedProfile = resolveMcpProfile(
|
|
661
|
+
options.agentType,
|
|
662
|
+
options.requestedProfile,
|
|
663
|
+
);
|
|
539
664
|
// Codex에 실제 등록된 서버만 대상으로 config override 생성.
|
|
540
665
|
// 미등록 서버에 enabled=false를 보내면 "invalid transport" 에러 발생.
|
|
541
666
|
const registeredServers = parseAvailableServers(options.availableServers);
|
|
@@ -546,14 +671,15 @@ export function getCodexMcpConfig(options = {}) {
|
|
|
546
671
|
}
|
|
547
672
|
const targetServers = registeredServers;
|
|
548
673
|
|
|
549
|
-
if (resolvedProfile ===
|
|
674
|
+
if (resolvedProfile === "none") {
|
|
550
675
|
// Codex 0.115+: transport 없는 서버에 enabled=false를 보내면 "invalid transport" 에러.
|
|
551
676
|
// 비허용 서버는 override에서 제외하고, 허용 서버만 명시적으로 설정한다.
|
|
552
677
|
return { mcp_servers: {} };
|
|
553
678
|
}
|
|
554
679
|
|
|
555
680
|
const config = { mcp_servers: {} };
|
|
556
|
-
const allowedToolsByServer =
|
|
681
|
+
const allowedToolsByServer =
|
|
682
|
+
getProfileDefinition(resolvedProfile).allowedToolsByServer;
|
|
557
683
|
for (const server of targetServers) {
|
|
558
684
|
// Codex 0.115+: transport 없는 서버에 enabled=false를 보내면 "invalid transport" 에러.
|
|
559
685
|
// 비허용 서버는 override에서 제외한다 (Codex 기본 설정이 유지됨).
|
|
@@ -571,15 +697,16 @@ export function getCodexMcpConfig(options = {}) {
|
|
|
571
697
|
|
|
572
698
|
function toTomlLiteral(value) {
|
|
573
699
|
if (Array.isArray(value)) {
|
|
574
|
-
return `[${value.map((item) => toTomlLiteral(item)).join(
|
|
700
|
+
return `[${value.map((item) => toTomlLiteral(item)).join(",")}]`;
|
|
575
701
|
}
|
|
576
|
-
if (typeof value ===
|
|
577
|
-
if (typeof value ===
|
|
702
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
703
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
704
|
+
return String(value);
|
|
578
705
|
throw new Error(`지원하지 않는 TOML 값 타입: ${typeof value}`);
|
|
579
706
|
}
|
|
580
707
|
|
|
581
708
|
function flattenConfig(prefix, value, output) {
|
|
582
|
-
if (value && typeof value ===
|
|
709
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
583
710
|
for (const [key, nestedValue] of Object.entries(value)) {
|
|
584
711
|
flattenConfig(prefix ? `${prefix}.${key}` : key, nestedValue, output);
|
|
585
712
|
}
|
|
@@ -591,7 +718,7 @@ function flattenConfig(prefix, value, output) {
|
|
|
591
718
|
export function getCodexConfigOverrides(options = {}) {
|
|
592
719
|
const config = getCodexMcpConfig(options);
|
|
593
720
|
const overrides = [];
|
|
594
|
-
flattenConfig(
|
|
721
|
+
flattenConfig("", config, overrides);
|
|
595
722
|
return overrides;
|
|
596
723
|
}
|
|
597
724
|
|
|
@@ -599,7 +726,10 @@ export function buildMcpPolicy(options = {}) {
|
|
|
599
726
|
const inventory = options.inventory || loadInventory(options.inventoryFile);
|
|
600
727
|
const inventoryIndex = buildInventoryIndex(inventory);
|
|
601
728
|
const resolvedOptions = { ...options, inventory, inventoryIndex };
|
|
602
|
-
const resolvedProfile = resolveMcpProfile(
|
|
729
|
+
const resolvedProfile = resolveMcpProfile(
|
|
730
|
+
options.agentType,
|
|
731
|
+
options.requestedProfile,
|
|
732
|
+
);
|
|
603
733
|
let allowedServers = resolveAllowedServers(resolvedOptions);
|
|
604
734
|
const hint = buildPromptHint(resolvedOptions);
|
|
605
735
|
|
|
@@ -612,9 +742,10 @@ export function buildMcpPolicy(options = {}) {
|
|
|
612
742
|
}
|
|
613
743
|
|
|
614
744
|
return {
|
|
615
|
-
requestedProfile:
|
|
616
|
-
|
|
617
|
-
|
|
745
|
+
requestedProfile:
|
|
746
|
+
typeof options.requestedProfile === "string" && options.requestedProfile
|
|
747
|
+
? options.requestedProfile
|
|
748
|
+
: "auto",
|
|
618
749
|
resolvedProfile,
|
|
619
750
|
resolvedPhase: phase || null,
|
|
620
751
|
allowedServers,
|
|
@@ -630,7 +761,7 @@ function shellEscape(value) {
|
|
|
630
761
|
}
|
|
631
762
|
|
|
632
763
|
function shellArray(name, values) {
|
|
633
|
-
return `${name}=(${values.map((value) => shellEscape(value)).join(
|
|
764
|
+
return `${name}=(${values.map((value) => shellEscape(value)).join(" ")})`;
|
|
634
765
|
}
|
|
635
766
|
|
|
636
767
|
export function toShellExports(policy) {
|
|
@@ -638,30 +769,33 @@ export function toShellExports(policy) {
|
|
|
638
769
|
`MCP_PROFILE_REQUESTED=${shellEscape(policy.requestedProfile)}`,
|
|
639
770
|
`MCP_RESOLVED_PROFILE=${shellEscape(policy.resolvedProfile)}`,
|
|
640
771
|
`MCP_HINT=${shellEscape(policy.hint)}`,
|
|
641
|
-
shellArray(
|
|
642
|
-
shellArray(
|
|
772
|
+
shellArray("GEMINI_ALLOWED_SERVERS", policy.geminiAllowedServers),
|
|
773
|
+
shellArray(
|
|
774
|
+
"CODEX_CONFIG_FLAGS",
|
|
775
|
+
policy.codexConfigOverrides.flatMap((override) => ["-c", override]),
|
|
776
|
+
),
|
|
643
777
|
`CODEX_CONFIG_JSON=${shellEscape(JSON.stringify(policy.codexConfig))}`,
|
|
644
778
|
];
|
|
645
779
|
if (policy.resolvedPhase) {
|
|
646
780
|
lines.push(`MCP_PIPELINE_PHASE=${shellEscape(policy.resolvedPhase)}`);
|
|
647
781
|
}
|
|
648
|
-
return lines.join(
|
|
782
|
+
return lines.join("\n");
|
|
649
783
|
}
|
|
650
784
|
|
|
651
785
|
function parseCliArgs(argv) {
|
|
652
786
|
const args = {
|
|
653
|
-
command:
|
|
654
|
-
agentType:
|
|
655
|
-
requestedProfile:
|
|
787
|
+
command: "json",
|
|
788
|
+
agentType: "",
|
|
789
|
+
requestedProfile: "auto",
|
|
656
790
|
availableServers: [],
|
|
657
|
-
inventoryFile:
|
|
658
|
-
searchTool:
|
|
659
|
-
taskText:
|
|
791
|
+
inventoryFile: "",
|
|
792
|
+
searchTool: "",
|
|
793
|
+
taskText: "",
|
|
660
794
|
workerIndex: undefined,
|
|
661
795
|
};
|
|
662
796
|
|
|
663
|
-
const [first =
|
|
664
|
-
if (!first.startsWith(
|
|
797
|
+
const [first = "json"] = argv;
|
|
798
|
+
if (!first.startsWith("--")) {
|
|
665
799
|
args.command = first;
|
|
666
800
|
argv = argv.slice(1);
|
|
667
801
|
}
|
|
@@ -676,28 +810,28 @@ function parseCliArgs(argv) {
|
|
|
676
810
|
};
|
|
677
811
|
|
|
678
812
|
switch (token) {
|
|
679
|
-
case
|
|
813
|
+
case "--agent":
|
|
680
814
|
args.agentType = next();
|
|
681
815
|
break;
|
|
682
|
-
case
|
|
816
|
+
case "--profile":
|
|
683
817
|
args.requestedProfile = next();
|
|
684
818
|
break;
|
|
685
|
-
case
|
|
819
|
+
case "--available":
|
|
686
820
|
args.availableServers = parseAvailableServers(next());
|
|
687
821
|
break;
|
|
688
|
-
case
|
|
822
|
+
case "--inventory-file":
|
|
689
823
|
args.inventoryFile = next();
|
|
690
824
|
break;
|
|
691
|
-
case
|
|
825
|
+
case "--search-tool":
|
|
692
826
|
args.searchTool = next();
|
|
693
827
|
break;
|
|
694
|
-
case
|
|
828
|
+
case "--task-text":
|
|
695
829
|
args.taskText = next();
|
|
696
830
|
break;
|
|
697
|
-
case
|
|
831
|
+
case "--worker-index":
|
|
698
832
|
args.workerIndex = Number.parseInt(next(), 10);
|
|
699
833
|
break;
|
|
700
|
-
case
|
|
834
|
+
case "--phase":
|
|
701
835
|
args.phase = next();
|
|
702
836
|
break;
|
|
703
837
|
default:
|
|
@@ -713,7 +847,9 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
713
847
|
try {
|
|
714
848
|
args = parseCliArgs(argv);
|
|
715
849
|
} catch (error) {
|
|
716
|
-
console.error(
|
|
850
|
+
console.error(
|
|
851
|
+
`[mcp-filter] ${error instanceof Error ? error.message : String(error)}`,
|
|
852
|
+
);
|
|
717
853
|
process.exitCode = 64;
|
|
718
854
|
return;
|
|
719
855
|
}
|
|
@@ -722,11 +858,13 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
722
858
|
try {
|
|
723
859
|
policy = buildMcpPolicy(args);
|
|
724
860
|
} catch (error) {
|
|
725
|
-
console.error(
|
|
861
|
+
console.error(
|
|
862
|
+
`[mcp-filter] ${error instanceof Error ? error.message : String(error)}`,
|
|
863
|
+
);
|
|
726
864
|
process.exitCode = 65;
|
|
727
865
|
return;
|
|
728
866
|
}
|
|
729
|
-
if (args.command ===
|
|
867
|
+
if (args.command === "shell") {
|
|
730
868
|
process.stdout.write(`${toShellExports(policy)}\n`);
|
|
731
869
|
return;
|
|
732
870
|
}
|