mcp-multisource
Provides tools for reading GitHub repositories, issues, pull requests, and code search, as well as commenting on issues.
Provides tools for listing Linear teams and issues, creating issues, and commenting on issues.
Provides tools for searching Notion, retrieving page text, and creating pages in a Notion workspace.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-multisourceReview the checkout rewrite spec and file Linear issues for gaps without PRs."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-multisource
An MCP server that federates GitHub, Notion and Linear behind one access-controlled tool surface, with per-source failure isolation.
It is built around a claim that is easy to state and annoying to implement: a model should be able to ask one question that spans three systems, get an answer when only two of them are up, and never be able to write to a system it was not scoped to.
┌──────────────────────────┐
MCP client ────────▶ │ AccessControlMiddleware │ role + resource scoping
(Claude Desktop) └────────────┬─────────────┘
│
┌────────────▼─────────────┐
│ tool surface │ 16 tools, each declaring
└──┬──────────┬──────────┬─┘ the capability it needs
│ │ │
┌──────▼───┐ ┌────▼─────┐ ┌──▼───────┐
│ GitHub │ │ Notion │ │ Linear │ one adapter each:
│ adapter │ │ adapter │ │ adapter │ own auth, own rate
└────┬─────┘ └────┬─────┘ └────┬─────┘ dialect, own breaker
│ │ │
REST REST+ver GraphQLWhat it does
Sixteen tools. Five read GitHub, three read/write Notion, four read/write Linear, one joins all three, three are metadata.
The tool that justifies the architecture is release_readiness:
release_readiness(repo="acme/service", spec_query="checkout rewrite", team_key="ENG")It fans out concurrently to open PRs and issues on GitHub, matching spec pages
in Notion, and tracked work in Linear — then returns a single object with a
degraded list naming any section it could not fill and why. A model calling
it during a Notion outage still gets the GitHub and Linear halves plus an
explicit note that the spec section is missing, instead of an error.
Tool | Source | Capability |
| GitHub | read |
| GitHub | read |
| GitHub | read |
| GitHub | read |
| GitHub | write |
| Notion | read |
| Notion | read |
| Notion | write |
| Linear | read |
| Linear | read |
| Linear | write |
| Linear | write |
| all three | read |
| — | read |
| — | read |
| — | admin |
Related MCP server: MCP-GET
Why these three sources
The brief for this project asks for a structured source, an unstructured knowledge source, and an action-capable source. That is not an arbitrary taxonomy — it is the minimum set needed to answer the question an engineering org actually asks, which is some variant of "is this thing done, and if not, what is left?"
Answering it requires three different kinds of lookup:
GitHub — structured ground truth. What the code actually says. PRs, issues, branches. Machine-shaped, queryable, and not subject to opinion.
Notion — unstructured intent. What the thing was supposed to do. Specs, runbooks, decision docs, postmortems. Prose written by humans for humans; there is no schema to query, so it has to be searched and flattened.
Linear — action. Where the gap between the first two becomes somebody's problem. This is the source the server can write to, which is why it is also the one with the tightest access control.
Each one is a genuinely different integration problem, which is the point. They do not share an auth scheme, a rate-limit dialect, an error convention, or even a protocol — Linear is GraphQL, the others are REST, and Notion's REST is version-pinned by header rather than by URL. Wrapping three REST APIs from the same vendor would demonstrate nothing. Every meaningful difference between these three forced a design decision that is documented below.
The use case they compose into: an engineering assistant. "Read the checkout rewrite spec, tell me which parts have no PR yet, and file Linear issues for the gaps." That single request touches unstructured knowledge, structured state, and a write — one from each column.
Authentication
Three sources, three schemes. None of them interchangeable, and two of them have a trap.
Source | Header | Notes |
GitHub |
| Also requires |
Notion |
| The version header is mandatory. Notion pins behaviour to a date string rather than negotiating; omit it and every call 400s. |
Linear |
| No |
The Notion sharing trap. An integration token that authenticates perfectly
still sees an empty workspace until a human explicitly shares each page with
it (page → ⋯ → Connections → <integration>). Notion reports this as
404 object_not_found, which is indistinguishable from a bad page id unless
you know. The adapter intercepts that specific code and rewrites the message to
name the real cause — see sources/notion.py. One line of code, an hour of
debugging saved, and a good example of why per-source adapters beat a generic
HTTP wrapper.
Credentials live in .env (gitignored), are read once in config.py, and are
never logged. source_diagnostics reports whether each credential is present
and what scheme it uses — never a value.
Access control
Most MCP examples expose every tool to every caller. That is fine for a demo and untenable the moment the server points at a company's real Linear workspace. This server gates on two axes.
Capabilities. Three roles, set by MCP_ROLE:
Role | read | write | admin |
| ✅ | ❌ | ❌ |
| ✅ | ✅ | ❌ |
| ✅ | ✅ | ✅ |
An unrecognised role fails closed to viewer rather than erroring or
defaulting open.
Resource scoping. Capability is not enough — a contributor allowed to write
should still not be able to write anywhere. ALLOWED_REPOS,
ALLOWED_LINEAR_TEAMS and ALLOWED_NOTION_PAGES scope a deployment to
specific resources. Empty means unscoped. Repo matching is case-insensitive
(GitHub is); Notion page ids are compared with dashes stripped, because Notion
hands out the same id in both forms.
Enforced in two places, on purpose. AccessControlMiddleware sits on the
SDK's ServerMiddleware hook, which runs before validation or handler dispatch
on every inbound request:
On
tools/listit filters the advertised tools. This is prompt hygiene — the model never burns a turn on a tool it cannot call.On
tools/callit authorizes independently. This is the actual boundary. A list filter is not security: a client can call any tool name it likes, including one it was never shown.tests/test_access.pyasserts exactly this case.
Requirements are declarative. Each tool carries a ToolAccess in its MCP
meta, so a new tool cannot silently ship without a policy — there is no
"remembered to add the check" failure mode:
register(linear_create_issue, ToolAccess(WRITE, "linear", "team_key", "linear_team"), ...)A denial comes back as a structured isError tool result carrying
retryable: false, not a protocol exception. The model reads why it was
denied and stops, instead of seeing a transport failure and retrying.
What happens when a source is unavailable
The design rule: a failing source degrades its own tools and nothing else. Breakers, rate budgets and clients are per adapter. There is no global state that one sick source can poison.
Error taxonomy
Every failure — an HTTP status, an httpx exception, a GraphQL errors array —
is normalised into one typed error before it leaves the adapter (errors.py).
The tool layer never branches on status codes.
Error | Retryable | Trips breaker | Raised for |
| ❌ | ❌ | 401, or 403 with quota remaining |
| ✅ | ✅ | 429, or GitHub's 403-with- |
| ✅ | ✅ | 5xx, DNS failure, connection reset, TLS error |
| ✅ | ✅ | exceeded |
| ❌ | ❌ | 404 / Notion |
| ❌ | ❌ | other 4xx, Linear GraphQL errors |
| ✅ | — | local refusal; the source is not called |
Non-retryable errors deliberately do not trip the breaker. A bad token or a typo'd repo name is not evidence that GitHub is down. Counting them would open the circuit on a typo and take out a healthy source.
Retry and backoff
Retryable errors are retried up to MAX_RETRIES with exponential backoff and
full jitter — uniform over [0, min(8s, 0.5 · 2ⁿ)). Full jitter rather than
fixed backoff because several tools hitting the same rate-limited source would
otherwise collide again on every retry wave. When the server sends
Retry-After, that number wins over our guess. Both delta-seconds and
HTTP-date forms are parsed.
Circuit breaker
Per source, three states:
closed ──(N consecutive retryable failures)──▶ open
▲ │
│ (cooldown elapses)
│ ▼
└────────(probe succeeds)──────────────── half_open ──(probe fails)──▶ openhalf_open admits exactly one probe; concurrent callers are refused locally
rather than all stampeding a recovering source. While open, calls fail in
microseconds without touching the network — which is the point: one dead source
must not consume the latency budget of a request that also touches two healthy
ones.
Per-source failure behaviour
Scenario | What the server does | What the model sees |
GitHub primary rate limit (403, | reads reset timestamp, sleeps that long, retries |
|
GitHub search rate limit (30/min, separate pool) | same, but only search tools are affected — core quota is untouched |
|
GitHub 404 from a fine-grained token's blind spot | no retry |
|
Notion 429 (~3 req/s) | honours |
|
Notion page not shared with the integration | no retry |
|
Notion 5xx | retried with jittered backoff, breaker counts it |
|
Linear GraphQL | mapped to | the correct typed error, not a false success |
Linear API key sent with a | no retry |
|
Any source down repeatedly | breaker opens; calls refused locally for |
|
Any source not configured at all | its tools are hidden from | tool does not exist; |
Unexpected exception inside a tool | caught in |
|
That last row matters more than it looks. An unhandled exception in an MCP
tool can take down the transport, which kills every other source too. _guard
wraps every source-touching tool for exactly that reason.
Observability
system_health probes all sources concurrently and returns, per source:
reachable, circuit state, consecutive failures, success/failure/locally-rejected
counts, rate budget, and last error kind. Where a source publishes no quota
headers — Notion — it reports null rather than inventing a number.
{
"healthy": ["github", "linear"],
"degraded": ["notion"],
"not_configured": [],
"server_usable": true
}Setup
git clone <this repo> && cd mcp-multisource
make setup
cp .env.example .env # then fill it in
make test # offline: access control, breaker, degradation
make e2e # protocol: real stdio, fake creds, no secrets needed
make verify # live: hits all three real APIsGetting credentials:
GitHub — Settings → Developer settings → Personal access tokens → Fine-grained. Grant
Contents: read,Issues: read+write,Metadata: read,Pull requests: read.Notion — notion.so/my-integrations → new internal integration → copy the secret. Then share at least one page with it, or it sees nothing.
Linear — Settings → API → Personal API keys.
Register with an MCP client
{
"mcpServers": {
"multisource": {
"command": "/absolute/path/to/mcp-multisource/.venv/bin/python",
"args": ["-m", "mcp_multisource"],
"cwd": "/absolute/path/to/mcp-multisource"
}
}
}.env is read relative to the project root, so cwd matters.
Verification
Three suites, testing different things.
make test — offline, no secrets. 39 tests using httpx.MockTransport, so
real adapter code runs against synthetic responses with no network. Covers the
capability matrix, scoping, the list-filter-versus-boundary distinction, every
row of the error taxonomy above, breaker state transitions, retry semantics,
and cross-source isolation.
make e2e — protocol, no secrets. Everything else tests Python objects.
This launches the server over real stdio and drives it with a real MCP client,
using deliberately invalid credentials — so it checks the handshake, the wire
format, and degradation in one pass. Observed output:
Role-based tool visibility
PASS viewer sees no write tools [11 tools]
PASS viewer sees no admin tools
PASS admin sees every tool [16 tools]
PASS capabilities differ
Enforcement is independent of the list filter
PASS hidden tool call is refused [access_denied]
PASS denial is marked non-retryable
Partial configuration
PASS unconfigured sources' tools are absent [9 tools]
PASS health names them as not_configured
Degradation with invalid credentials
PASS a failing tool returns a structured result, not an exception [auth_failed]
PASS the failure names its source
PASS cross-source tool still returns
PASS and reports what it could not reach [open_issues, open_pull_requests, tracked_work]
PASS marking itself incompleteThat fourth block is the brief's question answered concretely: with GitHub
returning 401 and the other two sources unconfigured, release_readiness still
returns ok: true with a populated degraded list rather than raising.
make verify — live. Authenticates against all three real APIs, runs a
real read per source, optionally (--write) creates real records, then
deliberately breaks things: invalid credentials per source asserting the
error taxonomy, and a repeated failure run asserting the breaker opens. Exits
nonzero on failure, so it works in CI.
Layout
src/mcp_multisource/
server.py tool surface; every source call wrapped in _guard
middleware.py AccessControlMiddleware — the enforcement seam
access.py capabilities, roles, resource scoping
resilience.py circuit breaker, jittered backoff, retry loop
errors.py the typed error taxonomy
registry.py builds adapters from settings; concurrent health probes
config.py the only module that reads os.environ
sources/
base.py shared HTTP: timeouts, retry, breaker, status→error mapping
github.py Bearer PAT; x-ratelimit-*; 403-means-rate-limit quirk
notion.py Bearer + version header; sharing trap; no quota headers
linear.py bare key; GraphQL; 200-with-errors dialect
scripts/verify_sources.py live verification + fault injection
scripts/e2e_check.py protocol smoke test over real stdio
tests/ offline suiteKnown limits
No pagination beyond the first page on any list tool. Fine for the queries these tools serve; would need cursors for exhaustive sync.
Notion block flattening covers the common spec/runbook shapes, not arbitrary nesting or database properties.
Roles come from an environment variable, i.e. one role per server process. A multi-tenant deployment would take identity from the transport (the SDK exposes
TokenVerifier/authfor this) rather than from config; the policy layer is already shaped for it — only the source ofrolechanges.Health is probed on demand, not on a background schedule.
Available Tools
3 toolsaccess_policyA
Show this session's role, capabilities and resource scopes.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full disclosure burden. It clearly signals a read-only, non-mutating operation via 'Show' and scopes the result to the current session. It does not discuss error conditions or side effects, but for a zero-parameter introspection tool this is adequate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single focused sentence with no filler. It front-loads the action ('Show') and the important scoping information ('this session's role, capabilities and resource scopes') immediately.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With zero parameters, an output schema, and a simple introspection purpose, the description provides all essential selection and invocation information. Nothing material is missing for an agent to understand when and how to call this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has no parameters, so there is no parameter semantics burden. The schema is trivially complete, and the description cannot add parameter-level meaning. The baseline score of 4 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb 'Show' and identifies exactly what is exposed: this session's role, capabilities, and resource scopes. This clearly distinguishes the tool from sibling tools like release_readiness and system_health, which focus on different concerns.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description makes the usage context easy to infer: an agent needing session access details would choose this tool. However, it does not explicitly state when to use this tool versus alternatives, nor does it mention any exclusions or prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
release_readinessB
Cross-source snapshot: open GitHub PRs/issues for a repo, matching Notion specs, and Linear tracked work. Returns partial results with a degraded list when a source is unavailable.
| Name | Required | Description | Default |
|---|---|---|---|
| repo | Yes | ||
| team_key | No | ||
| spec_query | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden; it discloses the notable degraded/partial-result behavior and the `degraded` list, which is valuable. It does not state read-only status or permissions, but the aggregation context and failure-mode disclosure provide a solid behavioral profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no filler; the core aggregation purpose is front-loaded and the degraded behavior is clearly stated. Very efficient for the information conveyed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema covers return shape, so the description can focus on invocation context, but the ambiguous 'matching Notion specs' phrasing and lack of any guidance on optional parameters leave real gaps. The degraded-source caveat is good, yet an agent still does not know how to supply or shape `spec_query` or `team_key`.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must explain the parameters, but it only loosely references Notion specs and Linear work without mapping to `spec_query` or `team_key`. `repo` is implied by 'for a repo', but optional params and their string formats remain unexplained.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description identifies a specific aggregation task—open GitHub PRs/issues, Notion specs, and Linear tracked work for a repo—so an agent can infer the tool's role. It lacks a direct verb and does not contrast with the unrelated sibling tools, but the resource and actions are sufficiently concrete.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'Cross-source snapshot' implies this is the go-to tool for combined release-readiness data, and the degraded-note signals when partial results should be expected. No explicit when-to-use/when-not-to-use guidance or alternative routing is provided, though the siblings appear unrelated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
system_healthC
Per-source health: reachable, circuit state, rate budget, last error.
| Name | Required | Description | Default |
|---|---|---|---|
| probe | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It adds useful per-source scope and lists the health dimensions, but it does not say whether the call is read-only, whether probe triggers active network checks or side effects, or how circuit state and rate budget should be interpreted.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single compact fragment with no filler; the colon list is easy to scan and front-loads the core idea. It is arguably too sparse for full guidance, but the brevity itself is well executed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-optional-parameter tool with an output schema, the core purpose is stated, but the probe parameter and safety/behavioral context are missing. It is minimally viable but leaves gaps an agent must guess around.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description never mentions the probe parameter, and schema description coverage is 0%, so the description adds no semantics beyond the schema's type and default. The parameter name is mildly self-explanatory, but the definition fails to state what different probe values actually do.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The phrase 'Per-source health' identifies the resource as source-level health status and enumerates what it covers: reachability, circuit state, rate budget, and last error. It lacks an explicit verb like 'get' or 'check,' so it is clear but not maximally precise and does not explicitly distinguish itself from release_readiness or access_policy.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is given on when to call this tool versus its siblings release_readiness or access_policy. There are no exclusion criteria, prerequisites, or context cues, so the agent must infer when this tool is the right choice.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
3 tool updates
v0.1.0- First observed
access_policy - First observed
release_readiness - First observed
system_health
TDQS
Each tool addresses a completely distinct concern: release_readiness provides aggregated cross-source data, system_health reports on source connectivity/status, and access_policy describes permissions. There is no overlap or ambiguity between their purposes.
All tool names follow the same snake_case noun_noun pattern (release_readiness, system_health, access_policy), which is predictable and readable. No mixed conventions or vague verbs appear.
Three tools is a well-scoped count for a focused multisource status server. Each tool earns its place and the set feels neither bloated nor too thin.
The server covers the core lifecycle of reading cross-source release readiness, monitoring source health, and checking access. A minor gap is the lack of a tool to query a specific source in detail or perform any mutating action, but agents can work around this with release_readiness and system_health.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
- mcpOAuthcom.vibgrate
Query your team's drift, vulnerability, and upgrade data from any AI assistant. OAuth 2.1, 51 tools.
SaaS intelligence for AI agents. 5 unified tools cover 1,000+ services with 91-96% token savings.
One MCP endpoint for Claude, GPT & Gemini: 100+ tools + no-code connectors + agent workers.
Task management for people and AI agents, with scoped OAuth access to issues, projects, and docs.
Related MCP Servers
- FlicenseBqualityDmaintenanceEnables AI assistants to interact with GitHub repositories, Confluence documentation, and Databricks Unity Catalog through comprehensive tools for code exploration, documentation retrieval, and data schema management.19-
- AlicenseNot gradedqualityCmaintenanceA unified Model Context Protocol server that provides a consistent interface for AI assistants to interact with productivity tools like Linear, GitHub, Slack, and Notion. It enables users to search, retrieve, and manage tasks and data across multiple workplace services from a single endpoint.16MIT
- FlicenseNot gradedqualityCmaintenanceProvides read-only access to Confluence, GitHub, Jira, Figma, Outlook, Teams, and browser automation for AI clients, enabling safe discovery, retrieval, and summarization of company knowledge without modifying source systems.-
- AlicenseAqualityAmaintenanceSearch, document and execute authenticated API calls across all your apps (Gmail, Slack, Stripe, Notion, GitHub, and more) through 4 universal tools whose context footprint stays constant no matter how many connections you add. Hosted remote server with OAuth at https://mcp.withone.ai/mcp, or run locally via npx @withone/mcp.439310MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/arpitpaliwal007/mcp-multisource'
If you have feedback or need assistance with the MCP directory API, please join our Discord server