Model Context Protocol Servers
This report summarizes scanner evidence for modelcontextprotocol/servers, a TypeScript project with 91,119 GitHub stars. SynthScan examined 25,109 lines of code across 137 source files, recording 85 pattern matches distributed across 11 syntactic categories. The legacy adjusted pattern signal is 4.5 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 73 finding(s).
Are imports declared and package/API names verified? 8 finding(s).
Are wrappers, guards, and repeated structures necessary? 59 finding(s).
Which changes need focused human review? 85 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 85 distinct pattern matches across 11 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/memory/index.ts | 690 | export function registerKnowledgeGraphResource( | CODE |
| LOW | src/memory/index.ts | 719 | export function registerKnowledgeGraphSubscriptions(server: McpServer) { | CODE |
| LOW | src/everything/tools/simulate-research-query.ts | 325 | function getInterpretationsForTopic( | CODE |
| LOW | src/everything/__tests__/tools.test.ts | 1059 | function createMockServerWithTasks() { | CODE |
| LOW | src/filesystem/path-validation.ts | 11 | export function isPathWithinAllowedDirectories(absolutePath: string, allowedDirectories: string[]): boolean { | CODE |
| LOW | src/filesystem/lib.ts | 77 | function resolveRelativePathAgainstAllowedDirectories(relativePath: string): string { | CODE |
| LOW | src/filesystem/lib.ts | 100 | async function resolveUnicodeEquivalentPath(absolutePath: string): Promise<string> { | CODE |
| LOW | src/filesystem/lib.ts | 442 | export async function searchFilesWithValidation( | CODE |
| LOW | src/filesystem/index.ts | 725 | async function updateAllowedDirectoriesFromRoots(requestedRoots: Root[]) { | CODE |
| LOW | src/time/test/time_server_test.py | 85 | def test_get_current_time_with_invalid_timezone(): | CODE |
| LOW⚡ | src/time/test/time_server_test.py | 465 | def test_get_local_tz_with_override(): | CODE |
| LOW⚡ | src/time/test/time_server_test.py | 472 | def test_get_local_tz_with_invalid_override(): | CODE |
| LOW⚡ | src/time/test/time_server_test.py | 479 | def test_get_local_tz_with_valid_iana_name(mock_get_localzone): | CODE |
| LOW⚡ | src/time/test/time_server_test.py | 488 | def test_get_local_tz_when_none_returned(mock_get_localzone): | CODE |
| LOW⚡ | src/time/test/time_server_test.py | 496 | def test_get_local_tz_handles_windows_timezones(mock_get_localzone): | CODE |
| LOW | src/time/test/time_server_test.py | 523 | def test_get_local_tz_various_timezones(mock_get_localzone, timezone_name): | CODE |
| LOW | src/fetch/tests/test_server.py | 29 | def test_url_with_query_params(self): | CODE |
| LOW | src/fetch/tests/test_server.py | 84 | def test_empty_content_returns_error(self): | STRING |
| LOW | src/fetch/tests/test_server.py | 149 | async def test_allows_when_robots_txt_allows_all(self): | CODE |
| LOW | src/fetch/tests/test_server.py | 168 | async def test_blocks_when_robots_txt_disallows_all(self): | CODE |
| LOW | src/fetch/tests/test_server.py | 247 | async def test_fetch_json_returns_raw(self): | CODE |
| LOW | src/fetch/src/mcp_server_fetch/server.py | 27 | def extract_content_from_html(html: str) -> str: | CODE |
| LOW | src/fetch/src/mcp_server_fetch/server.py | 66 | async def check_may_autonomously_fetch_url(url: str, user_agent: str, proxy_url: str | None = None) -> None: | CODE |
| LOW⚡ | src/git/tests/test_server.py | 285 | def test_validate_repo_path_no_restriction(): | CODE |
| LOW⚡ | src/git/tests/test_server.py | 290 | def test_validate_repo_path_exact_match(tmp_path: Path): | CODE |
| LOW⚡ | src/git/tests/test_server.py | 297 | def test_validate_repo_path_subdirectory(tmp_path: Path): | CODE |
| LOW⚡ | src/git/tests/test_server.py | 306 | def test_validate_repo_path_outside_allowed(tmp_path: Path): | CODE |
| LOW⚡ | src/git/tests/test_server.py | 330 | def test_validate_repo_path_symlink_escape(tmp_path: Path): | CODE |
| LOW⚡ | src/git/tests/test_server.py | 346 | def test_git_diff_rejects_flag_injection(test_repository): | CODE |
| LOW⚡ | src/git/tests/test_server.py | 482 | def test_git_create_branch_rejects_flag_injection(test_repository): | CODE |
| LOW⚡ | src/git/tests/test_server.py | 491 | def test_git_create_branch_rejects_base_branch_flag_injection(test_repository): | CODE |
| LOW⚡ | src/git/tests/test_server.py | 497 | def test_git_log_rejects_timestamp_flag_injection(test_repository): | CODE |
| LOW⚡ | src/git/tests/test_server.py | 506 | def test_git_branch_rejects_contains_flag_injection(test_repository): | CODE |
| LOW⚡ | src/git/tests/test_server.py | 515 | def test_git_log_formatting_no_repr(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 37 | def test_git_checkout_existing_branch(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 44 | def test_git_checkout_nonexistent_branch(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 77 | def test_git_branch_not_contains(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 101 | def test_git_add_specific_files(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 114 | def test_git_add_rejects_path_traversal(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 129 | def test_git_add_rejects_absolute_path_outside(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 155 | def test_git_diff_unstaged_empty(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 170 | def test_git_diff_staged_empty(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 247 | def test_git_create_branch_from_base(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 273 | def test_git_show_initial_commit(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 318 | def test_validate_repo_path_traversal_attempt(tmp_path: Path): | CODE |
| LOW | src/git/tests/test_server.py | 358 | def test_git_checkout_rejects_flag_injection(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 370 | def test_git_diff_allows_valid_refs(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 396 | def test_git_checkout_allows_valid_branches(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 414 | def test_git_diff_rejects_malicious_refs(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 440 | def test_git_checkout_rejects_malicious_refs(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 460 | def test_git_show_rejects_flag_injection(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 469 | def test_git_show_rejects_malicious_refs(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 536 | def test_git_log_filtered_unfiltered_parity(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 555 | def test_git_log_date_filtering(test_repository): | CODE |
| LOW | src/git/tests/test_server.py | 570 | def test_serve_run_does_not_raise_exceptions(tmp_path: Path): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | src/git/tests/test_server.py | 337 | # Create a symlink inside allowed that points outside | COMMENT |
| MEDIUM | src/git/tests/test_server.py | 66 | # Create a new branch and commit to it | COMMENT |
| MEDIUM | src/git/tests/test_server.py | 80 | # Create a new branch and commit to it | COMMENT |
| MEDIUM | src/git/tests/test_server.py | 375 | # Create a branch with a commit for diffing | COMMENT |
| MEDIUM | src/git/tests/test_server.py | 401 | # Create a branch to checkout | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | src/memory/__tests__/knowledge-graph.test.ts | 336 | { name: 'Acme Corp', entityType: 'company', observations: ['tech company'] }, | CODE |
| LOW⚡ | src/memory/__tests__/knowledge-graph.test.ts | 340 | { from: 'Alice', to: 'Acme Corp', relationType: 'works_at' }, | CODE |
| LOW⚡ | src/memory/__tests__/knowledge-graph.test.ts | 341 | { from: 'Bob', to: 'Acme Corp', relationType: 'competitor' }, | CODE |
| LOW | src/memory/__tests__/knowledge-graph.test.ts | 354 | expect(result.entities[0].name).toBe('Acme Corp'); | CODE |
| LOW | src/memory/__tests__/knowledge-graph.test.ts | 382 | expect(result.relations[0].to).toBe('Acme Corp'); | CODE |
| LOW | src/everything/__tests__/tools.test.ts | 640 | name: 'John Doe', | CODE |
| LOW | src/everything/__tests__/tools.test.ts | 662 | expect(result.content[1].text).toContain('John Doe'); | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/filesystem/path-validation.ts | 71 | // On Windows, we need to check if both paths are on the same drive | COMMENT |
| LOW⚡ | src/filesystem/__tests__/path-validation.test.ts | 949 | // Step 1: validatePath would pass for legitimate file | COMMENT |
| LOW⚡ | src/filesystem/__tests__/path-validation.test.ts | 952 | // Step 2: Race condition - replace file with symlink after validation | COMMENT |
| LOW⚡ | src/filesystem/__tests__/path-validation.test.ts | 956 | // Step 3: Read operation follows symlink to forbidden location | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | src/filesystem/__tests__/path-validation.test.ts | 949 | // Step 1: validatePath would pass for legitimate file | COMMENT |
| LOW⚡ | src/filesystem/__tests__/path-validation.test.ts | 952 | // Step 2: Race condition - replace file with symlink after validation | COMMENT |
| LOW⚡ | src/filesystem/__tests__/path-validation.test.ts | 956 | // Step 3: Read operation follows symlink to forbidden location | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | README.md | 123 | "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>" | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .github/ISSUE_TEMPLATE/1-bug_report.yml | 1 | # Bug report form (GitHub issue forms schema). | COMMENT |
| LOW | .github/ISSUE_TEMPLATE/2-feature_request.yml | 1 | # Feature request form (GitHub issue forms schema). | COMMENT |
| LOW | .github/ISSUE_TEMPLATE/config.yml | 1 | # Issue chooser configuration (GitHub issue template config schema). | COMMENT |
| LOW | src/everything/tools/trigger-url-elicitation.ts | 121 | // Key the one-shot error-path marker on inputs the client resends | COMMENT |
| LOW | src/everything/tools/trigger-url-elicitation.ts | 141 | // 1. The prerequisite points at a *different* URL than the one that | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | src/git/src/mcp_server_git/server.py | 459 | except git.InvalidGitRepositoryError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/time/src/mcp_server_time/server.py | 56 | except Exception as e: | CODE |
| LOW | src/time/src/mcp_server_time/server.py | 215 | except Exception as e: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/git/src/mcp_server_git/server.py | 247 | # Check if repo_path is the same as or a subdirectory of allowed_repository | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/release.py | 59 | def update_version(self, version: Version) -> None: ... | CODE |