跳到主要内容

Directory Structure

How backend code is organized in this project.


Overview​

Viben backend consists of two main components:

  1. Core Package (backend/browse-mcp): Main application with built-in data sources
  2. Plugin Packages (backend/plugins/*): Third-party extensible data sources

The architecture uses stevedore for plugin discovery via Python entry points.


Directory Layout​

backend/
├── browse-mcp/ # Core package
│ ├── browse_mcp/
│ │ ├── __init__.py
│ │ ├── base.py # BaseSearcher interface
│ │ ├── plugin.py # Plugin discovery & loading
│ │ ├── api/ # API endpoints
│ │ ├── sources/ # Built-in data sources (20+)
│ │ │ ├── arxiv.py
│ │ │ ├── pubmed.py
│ │ │ ├── semantic.py
│ │ │ ├── ieee.py
│ │ │ └── ...
│ │ ├── utils/ # Utilities
│ │ └── models/ # Data models
│ ├── pyproject.toml
│ ├── README.md
│ └── tests/
│
└── plugins/ # Plugin packages
├── browse-mcp-plugin-context7/
│ ├── browse_mcp_plugin_context7/
│ │ ├── __init__.py
│ │ └── searcher.py # Context7 searcher implementation
│ ├── pyproject.toml # Entry point registration
│ ├── README.md
│ └── tests/
│
└── browse-mcp-plugin-social-media/
├── browse_mcp_plugin_social_media/
│ ├── __init__.py
│ ├── twitter.py # Twitter searcher
│ └── linkedin.py # LinkedIn searcher
├── pyproject.toml
├── README.md
└── tests/

Module Organization​

Core Package (backend/browse-mcp)​

Purpose: Main application with built-in academic/research data sources

Key Modules:

ModulePurposeExamples
base.pyBase classes and interfacesBaseSearcher
plugin.pyPlugin discovery with stevedorediscover_searchers()
api/API endpoints and routesREST API handlers
sources/Built-in data source implementationsarXiv, PubMed, Semantic Scholar
utils/Helper functionsHTTP clients, parsers
models/Data models and schemasPaper, Author, Citation

Built-in Sources (20+):

  • Academic: arxiv, pubmed, pmc, biorxiv, medrxiv, semantic, core, crossref, iacr
  • Publishers: acm, ieee, sciencedirect, springer, scopus, jstor
  • General: google_scholar, researchgate, wos, sci_hub, hub

Plugin Packages (backend/plugins/*)​

Purpose: Third-party extensible data sources

Structure Pattern:

browse-mcp-plugin-{name}/
├── browse_mcp_plugin_{name}/ # Package with underscores
│ ├── __init__.py
│ └── searcher.py # Searcher implementation
├── pyproject.toml # Entry point registration
├── README.md
└── tests/

Entry Point Registration:

[tool.poetry.plugins."browse_mcp.searchers"]
entry_name = "browse_mcp_plugin_name.searcher:SearcherClass"

Available Plugins:

  • browse-mcp-plugin-context7: Context7 web searcher
  • browse-mcp-plugin-social-media: Twitter, LinkedIn searchers

Naming Conventions​

Package Names​

TypePatternExample
Core packagebrowse-mcpbackend/browse-mcp
Plugin packagebrowse-mcp-plugin-{name}browse-mcp-plugin-context7
Python moduleUse underscoresbrowse_mcp_plugin_context7

Note: Package names use hyphens (-), Python modules use underscores (_).

File Names​

TypePatternExample
Data sourceLowercase with underscoresarxiv.py, google_scholar.py
Base classesDescriptivebase.py, plugin.py
API routesFeature-basedsearch.py, papers.py
Teststest_ prefixtest_arxiv.py

Class Names​

TypePatternExample
Searcher{Name}SearcherArxivSearcher, PubMedSearcher
ModelPascalCasePaper, Author, Citation
Exception{Name}ErrorAPIError, SearchError

Entry Point Names​

PatternExampleProvider Name
Lowercase with underscoresarxivbrowse-mcp/arxiv
Descriptivesemantic_scholarbrowse-mcp/semantic_scholar
Plugin prefixcontext7_webcontext7/web

Provider Naming Format: {provider}/{source_name}


Module Dependencies​

Core Package Dependencies​

browse_mcp/
├── base.py # No internal dependencies
├── plugin.py # Depends on: base.py
├── sources/
│ └── *.py # Depends on: base.py
├── api/
│ └── *.py # Depends on: plugin.py, sources/
└── utils/
└── *.py # No internal dependencies (utilities only)

Dependency Rules:

  1. base.py has no internal dependencies (foundation)
  2. All searchers depend on base.py only
  3. plugin.py orchestrates searcher discovery
  4. API layer depends on plugin system
  5. Utils are standalone (no circular dependencies)

Plugin Package Dependencies​

browse_mcp_plugin_name/
├── searcher.py # Depends on: browse_mcp.base
└── utils.py # Plugin-specific utilities

Rules:

  • Plugins only depend on browse_mcp.base (public interface)
  • Plugins NEVER import from browse_mcp.sources (encapsulation)
  • Each plugin is independent (no cross-plugin dependencies)

Examples​

Example 1: Built-in Searcher​

File: backend/browse-mcp/browse_mcp/sources/arxiv.py

from browse_mcp.base import BaseSearcher
from typing import List, Dict

class ArxivSearcher(BaseSearcher):
"""arXiv preprint repository searcher."""

def __init__(self):
super().__init__(name="arxiv")
self.base_url = "http://export.arxiv.org/api/query"

def search(self, query: str, max_results: int = 10) -> List[Dict]:
"""Search arXiv for papers."""
# Implementation here
pass

def get_paper_details(self, paper_id: str) -> Dict:
"""Fetch detailed metadata for an arXiv paper."""
# Implementation here
pass

Example 2: Plugin Searcher​

File: backend/plugins/browse-mcp-plugin-context7/browse_mcp_plugin_context7/searcher.py

from browse_mcp.base import BaseSearcher
import os

class Context7Searcher(BaseSearcher):
"""Context7 web searcher plugin."""

def __init__(self):
super().__init__(name="context7")
self.api_key = os.getenv("CONTEXT7_API_KEY")

def search(self, query: str, **kwargs) -> List[Dict]:
# Context7 API implementation
pass

Entry Point: pyproject.toml

[tool.poetry.plugins."browse_mcp.searchers"]
context7_web = "browse_mcp_plugin_context7.searcher:Context7Searcher"

Example 3: Plugin Discovery​

File: backend/browse-mcp/browse_mcp/plugin.py

from stevedore import extension

def discover_searchers():
"""Discover all available searchers (built-in + plugins)."""
mgr = extension.ExtensionManager(
namespace='browse_mcp.searchers',
invoke_on_load=True,
)

searchers = {}
for ext in mgr:
searchers[ext.name] = ext.obj

return searchers

Adding New Components​

Adding a Built-in Source​

  1. Create file in backend/browse-mcp/browse_mcp/sources/{name}.py
  2. Implement BaseSearcher interface
  3. Register in provider.index.json
  4. Add tests in backend/browse-mcp/tests/test_{name}.py

Creating a Plugin​

  1. Create package: backend/plugins/browse-mcp-plugin-{name}/
  2. Implement searcher inheriting from BaseSearcher
  3. Register entry point in pyproject.toml
  4. Update provider.index.json
  5. Install plugin: cd backend/plugins/browse-mcp-plugin-{name} && poetry install

See: Plugin Architecture Guide for detailed instructions.


References​

  • Plugin Architecture: plugin-architecture.md
  • Provider Registry: provider.index.json (root directory)
  • Base Searcher: backend/browse-mcp/browse_mcp/base.py
  • Plugin System: backend/browse-mcp/browse_mcp/plugin.py

Last Updated: 2026-02-03