Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Bug report
description: Something in research-mcp doesn't work as documented or as expected.
labels: [bug]
body:
- type: markdown
attributes:
value: |
Thanks for filing a bug. The more concrete you can be about reproduction, the faster a fix lands.
For usage questions ("how do I…?") please use [Discussions](https://github.com/Burton-David/ResearchAssistantMCP/discussions) instead.

- type: textarea
id: summary
attributes:
label: What happened
description: One-paragraph summary. What did you do, what did you see, what did you expect?
placeholder: |
I called `assist_draft` with a 2000-character paragraph and got a 500-char validation error,
even though the docstring says drafts up to 20K chars are supported.
validations:
required: true

- type: textarea
id: reproduction
attributes:
label: How to reproduce
description: Minimal steps or code that reliably trigger the bug.
placeholder: |
1. Set `RESEARCH_MCP_OPENALEX_EMAIL=you@example.com`
2. Run `research-mcp serve`
3. Call `assist_draft` with text="<800 chars of academic prose>"
4. Observe error: <paste>
render: bash
validations:
required: true

- type: textarea
id: expected
attributes:
label: Expected behavior
description: What did you think would happen instead?
validations:
required: true

- type: input
id: version
attributes:
label: research-mcp version / commit
description: Output of `git rev-parse HEAD` or the pip-installed version.
placeholder: "main @ 626b223"
validations:
required: true

- type: input
id: python
attributes:
label: Python version
placeholder: "3.12.4"
validations:
required: true

- type: input
id: os
attributes:
label: Operating system
placeholder: "macOS 14.5 / Ubuntu 22.04 / Windows 11"
validations:
required: true

- type: textarea
id: logs
attributes:
label: Relevant logs
description: |
Paste any stderr / tool-call output / stack traces. Server logs go to stderr;
capture with `research-mcp serve 2>logs.txt`.
render: text
validations:
required: false

- type: textarea
id: context
attributes:
label: Additional context
description: Anything else that might matter — env vars, custom protocol implementations, recent changes.
validations:
required: false
11 changes: 11 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Usage question
url: https://github.com/Burton-David/ResearchAssistantMCP/discussions
about: How do I…? questions belong in Discussions, not issues.
- name: Roadmap items
url: https://github.com/Burton-David/ResearchAssistantMCP/issues?q=label%3Aroadmap
about: Existing roadmap items already triaged and labeled.
- name: Good first issues
url: https://github.com/Burton-David/ResearchAssistantMCP/issues?q=label%3A%22good+first+issue%22
about: Issues scoped for a single afternoon, no deep context needed.
58 changes: 58 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Feature request
description: Propose a new capability or behavior for research-mcp.
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
Thanks for proposing something. Please describe the **problem** first — concrete use cases are
easier to evaluate than concrete solutions. We may end up shipping a different shape than you
proposed, and that's fine.

Larger features should be discussed in [Discussions](https://github.com/Burton-David/ResearchAssistantMCP/discussions)
before a full issue if you're not sure about scope.

- type: textarea
id: problem
attributes:
label: What problem does this solve?
description: |
Describe the user-visible problem. What can't you do today, or what's awkward about doing it?
placeholder: |
When citing very old foundational papers (50+ years), the recency dimension dominates the score
and pushes them below adjacent newer-but-less-canonical work. The user has to fall back to
`search_papers` and pick manually.
validations:
required: true

- type: textarea
id: proposed
attributes:
label: Proposed solution (optional)
description: |
If you have an approach in mind, describe it. Don't worry about getting the design right —
the discussion in the issue will refine it.
validations:
required: false

- type: textarea
id: alternatives
attributes:
label: Alternatives considered (optional)
description: |
Other approaches you thought about and why you ruled them out, if applicable.
validations:
required: false

- type: dropdown
id: scope
attributes:
label: Estimated scope
description: Best guess; the maintainer will re-label if needed.
options:
- "Small (afternoon, single file)"
- "Medium (multi-file, may touch a protocol implementation)"
- "Large (new protocol, ADR-grade decisions)"
- "Not sure"
validations:
required: false
30 changes: 30 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!--
Thanks for the PR. A few conventions from CONTRIBUTING.md to keep in mind:

- Branch off latest origin/main; squash-merge on landing.
- One concern per branch — keep scope tight enough to review in one sitting.
- Real implementations of protocols we own, not mocks (see tests/conftest.py).
- Link any related issue with "Closes #N" so it auto-closes on merge.
-->

## Summary

<!-- One paragraph: what changed and why. The "why" matters more than the "what" -
reviewers can read the diff for the what. -->

## Test plan

<!-- What did you actually run locally? Check the boxes that apply; add anything else. -->

- [ ] `pytest -q` — all green
- [ ] `ruff check src tests` — clean
- [ ] `mypy src` — clean
- [ ] REPL-verified the new behavior (paste the snippet you ran, briefly)
- [ ] Manual MCP test (Claude Desktop, Claude Code, or a custom client) — if user-facing
- [ ] Other: _(describe)_

## Notes for the reviewer

<!-- Anything non-obvious about the design? Tradeoffs you considered and ruled out?
Subtle invariants the test suite doesn't cover? Drop them here so the reviewer
doesn't have to re-derive them from the diff. -->
38 changes: 29 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ export SEMANTIC_SCHOLAR_API_KEY=... # optional, for higher S2 limit
## Running the checks

```bash
pytest # 67 unit + 1 e2e tests, all offline
pytest -m unit # fast subset
RESEARCH_MCP_INTEGRATION=1 pytest # add the live arXiv tests
pytest # full suite, all offline
pytest -m unit # fast subset (no e2e)
RESEARCH_MCP_INTEGRATION=1 pytest # add the live arXiv / S2 / OpenAlex tests
ruff check src tests # lint
mypy src # type check (strict)
```

A green run on all four is the floor for any PR.
A green run on all four is the floor for any PR. CI runs the same checks on every push and PR.

## REPL-first development

Expand All @@ -43,20 +43,40 @@ research-mcp repl
>>> cite(papers[0], "ama")
```

If the API feels wrong inside the REPL, change the protocol BEFORE writing five files that depend on it. The four protocols in `src/research_mcp/domain/` are stable contracts — implementations conform to them, not the other way around. If a real implementation forces a protocol change, raise an issue first.
If the API feels wrong inside the REPL, change the protocol BEFORE writing five files that depend on it. The nine protocols in `src/research_mcp/domain/` are stable contracts — implementations conform to them, not the other way around. If a real implementation forces a protocol change, raise an issue first.

## Code conventions

- **Async everywhere I/O happens.** All four protocols are async; implementations must be too. No CPU-bound work in the protocols.
- **Async everywhere I/O happens.** The I/O-bound protocols (Source, Index, ClaimExtractor, CitationScorer, PaperAnalyzer) are async; implementations must be too. CPU-bound protocols (Chunker, CitationRenderer) stay sync — async there would be theatre.
- **Python 3.11+** syntax: `X | None`, `StrEnum`, structural pattern matching where it helps.
- **No `# type: ignore` without a comment** explaining the suppression. The two existing ignores point at the IPython entry point and the mcp SDK decorators; both note why.
- **No mocks of protocols you own.** Tests use lightweight real implementations (`FakeEmbedder`, `MemoryIndex`, `StaticSource`) — they live in `tests/conftest.py` so they stay close to the test.
- **No invented abstractions.** No "Pipeline", "Workflow", or "Manager" classes unless usage clearly demands them.
- **Comments are sparse.** Only when the *why* is non-obvious — never to restate what the code does.

## Adding a new Source / Index / Embedder / CitationRenderer

Implement the protocol in a new module under the matching package, write a test next to it that uses the protocol surface (no monkey-patching), and re-export from the package `__init__`. The wiring lives in `cli.py` and `mcp/server.py`; add your new implementation there if it should be selectable at runtime.
## Adding a new implementation

The nine protocols in `src/research_mcp/domain/` and where their implementations live:

| Protocol | What it does | Package |
|---|---|---|
| `Source` | Fetch papers from an upstream (arXiv, S2, PubMed, OpenAlex, ...) | `sources/` |
| `Index` | Persist and vector-search ingested papers | `index/` |
| `Embedder` | Turn text into vectors (OpenAI, sentence-transformers, ...) | `embedder/` |
| `CitationRenderer` | Format a paper as AMA / APA / BibTeX / MLA / Chicago | `citation/` |
| `Reranker` | Rescore search results (cross-encoder, learned, ...) | `reranker/` |
| `ClaimExtractor` | Pull typed claims from draft text (spaCy, LLM, ...) | `claim_extractor/` |
| `Chunker` | Split a paper into citation-grain chunks | `chunker/` |
| `CitationScorer` | Score a paper's quality for a claim (heuristic, field-aware, LLM, ...) | `citation_scorer/` |
| `PaperAnalyzer` | Structured paper analysis (summary, methodology, etc.) | `paper_analyzer/` |

Steps regardless of which protocol:

1. **Implement** in a new module under the matching package.
2. **Test next to it** — exercise the protocol surface, no monkey-patching, no mocks of protocols we own. Use the lightweight real implementations in `tests/conftest.py` (`FakeEmbedder`, `MemoryIndex`, `StaticSource`, etc.).
3. **Re-export** from the package `__init__.py` so callers can `from research_mcp.<package> import YourImpl`.
4. **Wire it up** in `cli.py` and `mcp/server.py` if it should be runtime-selectable via an env var. Most should — env-var selection is how users compose configurations without code changes.
5. **Document** the env-var name in the README's quick start.

## Commits

Expand Down
Loading