Skip to main content
Glama
README.md
# mcp-research-collective

> A multi-agent research system built on the **Model Context Protocol** (MCP). Specialized agents (Planner → Researcher → Reasoner) collaborate via a thread-safe **blackboard** of shared beliefs, exposed as MCP tools that can be wired into Claude Desktop or any other MCP host.

![Python](https://img.shields.io/badge/python-3.10%2B-blue)
![License](https://img.shields.io/badge/license-MIT-green)
![Local-first](https://img.shields.io/badge/runs-100%25%20local-success)
![MCP](https://img.shields.io/badge/protocol-MCP-7c3aed)
![Async](https://img.shields.io/badge/runtime-asyncio-009688)

## What this demonstrates

- **Model Context Protocol** — a real, runnable [FastMCP](https://github.com/jlowin/fastmcp) server with seven tools (`blackboard_set`, `blackboard_get`, `blackboard_query`, `blackboard_dump`, plus `add`/`subtract`/`multiply`/`divide`).
- **Blackboard architecture** — a thread-safe shared belief store (`Belief` dataclass + `Blackboard` class with `RLock`-protected upsert/read/query, TTLs, regex queries, and tag filters).
- **Multi-agent orchestration** — Planner decomposes the prompt → Researcher fetches facts from a JSON KB → Reasoner synthesizes the final answer. Agents only know about the connector, never each other; they coordinate solely through the blackboard.
- **Two interchangeable transports** — an in-process `MCPConnector` for fast notebook/test runs, and the actual STDIO `FastMCP` server for production / Claude Desktop integration.

## Tech stack

- [Model Context Protocol Python SDK](https://github.com/modelcontextprotocol/python-sdk) (`mcp[cli]`) — `FastMCP` server, `ClientSession`, STDIO transport
- Python 3.10+, `asyncio`, `threading.RLock`
- [pytest](https://docs.pytest.org/) for deterministic blackboard / agent tests
- **No LLM dependency** — the agents in this demo orchestrate over a structured KB. The MCP server is the surface that *you* (or any MCP host) can attach an LLM to.

## Architecture

```mermaid
flowchart TB
    U[User Prompt] --> P[PlannerAgent<br/>decomposes into subtasks]
    P -->|publishes 'task.list'| BB[(Blackboard<br/>thread-safe<br/>belief store)]
    BB -->|reads tasks| R[ResearchAgent<br/>fetches from kb/*.json]
    R -->|publishes belief.what / why / how| BB
    BB -->|reads beliefs| S[ReasonerAgent<br/>synthesizes answer]
    S -->|publishes answer.v1| BB
    S --> Out[Final answer<br/>+ full blackboard state]

    subgraph "MCP Surface"
        BB <-->|set/get/query/dump| MCP[FastMCP server<br/>STDIO transport]
        MCP <-->|JSON-RPC| Claude[Claude Desktop /<br/>any MCP host]
    end
```

## Quickstart

```bash
# 1. Python env
python -m venv .venv
source .venv/Scripts/activate   # Windows: .venv\Scripts\activate
pip install -e .[dev]

# 2. Run the deterministic tests
pytest

# 3. Run the in-process orchestrator from the CLI
mcp-research-orchestrator "What is a Faculty Senate, why have one, and how is it built?"

# 4. Run the standalone MCP server (for Claude Desktop or another MCP host)
mcp-research-collective
```

## Programmatic usage (in-process, no STDIO)

```python
from mcp_research_collective import run_collective

answer, blackboard_state_json = run_collective(
    "What is the role of a Faculty Senate and why have one?"
)

print(answer)
# # Synthesized Answer
#
# **What it is**
# - Elected body of faculty that represents the academic community...
# ...
# **Why it matters**
# - Ensures academic decisions are not solely administrative
# ...
```

## Use with Claude Desktop

This is the differentiator: the same code that runs the in-process demo is also a working MCP server you can connect to Claude Desktop. Add this to your `claude_desktop_config.json`:

**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "research-collective": {
      "command": "python",
      "args": ["-m", "mcp_research_collective.entrypoints.run_server"],
      "env": {
        "PYTHONPATH": "/absolute/path/to/mcp-research-collective/src"
      }
    }
  }
}
```

After restarting Claude Desktop, you'll see four `blackboard_*` tools and four math tools available in the Claude UI. Try:

> Use the `blackboard_set` tool to publish a fact about my project deadline, then `blackboard_query` to find it back.

## Adding a new research topic

Drop a JSON file in [src/mcp_research_collective/kb/](src/mcp_research_collective/kb/) with the schema:

```json
{
  "key": "topic.subkey",
  "what": ["fact 1", "fact 2"],
  "why":  ["fact 1", "fact 2"],
  "how":  ["fact 1", "fact 2"]
}
```

Then construct the orchestrator with `topic_key="topic.subkey"`. No code changes required.

## What I learned

The Planner→Researcher→Reasoner pattern with a blackboard makes each agent's responsibility small enough to reason about in isolation. The system dynamically scales its effort based on the prompt: asking only "what" and "why" results in the Planner publishing only `define.what` and `explain.why`. The Researcher fetches exactly what is needed, and the blackboard tracks this selective context gathering.

The biggest win wasn't the MCP wire format — it was the discipline of forcing every state mutation through `mcp.call("blackboard.set", ...)`. That single chokepoint makes the whole system trivially observable: `dump_state()` shows exactly what each agent contributed, when, and with what confidence. Adding a fourth agent (like a Critic or a Cache) is straightforward because the contract is simply "read tagged beliefs, write tagged beliefs."

## Project layout

```
mcp-research-collective/
├── src/mcp_research_collective/
│   ├── blackboard.py        # Belief + thread-safe Blackboard
│   ├── connector.py         # In-process MCPConnector (set/get/query/dump)
│   ├── kb/*.json            # Knowledge base topics (drop-in JSON files)
│   ├── knowledge_base.py    # Loader for kb/*.json
│   ├── agents.py            # PlannerAgent, ResearchAgent, ReasonerAgent
│   ├── orchestrator.py      # CollectiveOrchestrator + run_collective
│   ├── mcp_server.py        # FastMCP server exposing blackboard + math tools
│   └── entrypoints/
│       ├── run_server.py        # `python -m ... run_server`
│       └── run_orchestrator.py  # `python -m ... run_orchestrator "<q>"`
├── tests/                   # blackboard + connector + orchestrator + KB tests
└── notebooks/demo.ipynb     # walkthrough with full blackboard state dump
```

TDQS

A3.6/5.0

Scored across 8 tools

Disambiguation5/5

All tools have clearly distinct purposes: the four arithmetic operations are separate, and the four blackboard operations (set, get, query, dump) each serve unique functions. There is no overlap or ambiguity.

Naming Consistency3/5

Arithmetic tools use single verbs (add, subtract, multiply, divide) while blackboard tools use a noun_verb pattern (blackboard_dump, blackboard_get, blackboard_query, blackboard_set). This inconsistency in naming convention across the server reduces coherence.

Tool Count5/5

With 8 tools covering two distinct subdomains (arithmetic and blackboard), the count feels well-scoped. Each tool serves a clear purpose without unnecessary duplication or bloat.

Completeness4/5

The arithmetic set is complete for basic operations. The blackboard tools are mostly complete but lack a delete/remove operation, which could be a gap for typical blackboard use cases. Minor gap.

Maintenance

ActivityInactive
ResponsivenessNo issues