MCP Catalog Specification
Formal specification of the MCP (Model Context Protocol) server catalog used by
@aidokit/mcp-catalog. Defines the data shape, trigger grammar, role-scoping rules, security model, and the full v0.1 catalog.
| Field | Value |
|---|---|
| Spec name | aidokit MCP Catalog |
| Spec version | 1.0 (draft) |
| Status | Pre-alpha — surface frozen, entries grow over time |
| Implements | @aidokit/mcp-catalog package |
| Sibling specs | adapter-contract.md, stack-pack-contract.md |
| Last reviewed | Pre-publication |
Table of contents
- Purpose and audience
- Conventions
- Concepts
- Catalog structure
MCPDeftype- Supporting types
- Trigger grammar
- Install spec format
- Role scoping rules
- Security model
- The v0.1 catalog
- Custom (non-catalog) MCPs
- Catalog evolution
- Versioning
- Worked example: lookup → suggest → install
- Open questions
- Appendix A: Trigger evaluation reference
- Appendix B: Adding a new catalog entry
1. Purpose and audience
1.1 Purpose
This specification defines:
- The shape of an MCP catalog entry
- The trigger grammar that decides which MCPs to suggest in a given project context
- The install specification each adapter consumes
- Role-scoping rules that prevent capability creep
- The security model for sensitive MCPs
- The full v0.1 catalog contents
@aidokit/mcp-catalog is built directly from this spec. Engineers implementing the package use this document as the source of truth for both schema and data.
1.2 Audience
@aidokit/clidevelopers consuming the catalog- Adapter authors consuming
installspecs - Stack pack authors referencing MCP ids in
suggestMCPs - MCP server authors wanting their server listed in the catalog
- Core team evolving the catalog over time
1.3 Non-purpose
This document does NOT specify:
- The MCP protocol itself (defined upstream at https://modelcontextprotocol.io)
- Per-MCP server implementation (each MCP is a separate project)
- Adapter MCP installation mechanisms in depth (lives in
adapter-contract.md)
2. Conventions
2.1 RFC 2119 keywords
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY, and REQUIRED are interpreted as described in RFC 2119.
2.2 Type syntax
TypeScript syntax is normative for all types.
2.3 ID conventions
- MCP ids MUST be kebab-case
- MCP ids MUST be globally unique within the catalog
- MCP ids MUST be short (1–3 hyphenated words) and stable (once published, never renamed)
3. Concepts
| Term | Definition |
|---|---|
| MCP server | A Model Context Protocol server providing tools to AI agents |
| Catalog entry | A single MCPDef describing one MCP server |
| Trigger | A predicate against ProjectContext determining whether to suggest |
| Suggested-for role | A role the MCP is appropriate for (used for default scoping) |
| Security-sensitive | An MCP with elevated privileges requiring explicit opt-in |
| Install spec | Per-adapter instructions for registering the MCP with the target CLI |
| Custom MCP | An MCP not in the catalog, added via --custom-url |
4. Catalog structure
4.1 The catalog is data
The catalog is a TypeScript const array exported from @aidokit/mcp-catalog:
export const MCP_CATALOG: MCPDef[] = [
{
/* entry 1 */
},
{
/* entry 2 */
},
// ...
];
Adding an MCP is a one-file edit. There is no plugin discovery, no runtime registration, no dynamic loading. The catalog at runtime equals the catalog at build time.
4.2 Catalog is per-aidokit-version
The catalog ships with @aidokit/mcp-catalog and is versioned with that package. Users on different aidokit versions may see different catalog contents.
4.3 No deprecation, only addition
Catalog entries MUST NOT be removed once published. Entries MAY be marked deprecated: true (see §13). Removing an entry would silently break projects depending on it.
5. MCPDef type
The normative TypeScript type:
import type { RoleName } from '@aidokit/core';
export interface MCPDef {
/** Globally unique kebab-case identifier. */
id: string;
/** Human-readable display name. */
name: string;
/** One-line purpose description. */
purpose: string;
/** Roles for which this MCP is appropriate by default. */
suggestedFor: RoleName[];
/** Predicate expressions determining when to suggest. */
triggers: string[];
/** Whether explicit user confirmation is required even on add. */
securitySensitive: boolean;
/**
* Whether this server returns content sourced from untrusted third
* parties (web pages, search results, rendered DOM, fetched
* documentation). Agents must quarantine such output — treat as
* data, never instructions. See `security-model.md` §T18 / §7.12.
* Defaults to `false`/absent.
*/
untrustedOutput?: boolean;
/** Per-adapter installation instructions. */
install: Record<AdapterName, MCPInstallSpec>;
/** Whether this entry is deprecated. Suggestion lists exclude deprecated. */
deprecated?: boolean;
/** Replacement MCP id, if deprecated. */
replacedBy?: string;
/** Project URL for the MCP server (informational). */
homepage?: string;
/** Tags for marketplace browsing (informational). */
tags?: string[];
}
export type AdapterName = 'claude-code' | 'codex' | string;
5.1 Field semantics
| Field | Required | Notes |
|---|---|---|
id |
yes | Permanent; MUST NOT be renamed once published |
name |
yes | MAY be updated for clarity |
purpose |
yes | Plain prose; under 100 characters |
suggestedFor |
yes | MAY be empty for 'never'-triggered MCPs |
triggers |
yes | At least one trigger; see §7 for grammar |
securitySensitive |
yes | Defaults to false; see §10 |
untrustedOutput |
no | Defaults to false; see §5.2 |
install |
yes | MUST include at least one adapter |
deprecated |
no | Defaults to false |
replacedBy |
no | Required if deprecated: true |
homepage |
no | Informational; appears in aido mcp suggest --verbose |
tags |
no | Informational; used for filtering |
5.2 untrustedOutput — prompt-injection quarantine flag
When true, this server returns content from untrusted third parties
(web pages, search results, rendered DOM, fetched documentation). Such
content can carry instructions targeted at the consuming agent
(prompt-injection). Agents MUST quarantine the output — treat it as
data, never as instructions. See security-model.md
§T18 / §7.12 for the threat model and declared mitigation.
When aidokit doctor runs against a project that enables an
untrustedOutput: true server, it requires the
quarantining-untrusted-output skill to be present in the emitted
skill set; otherwise it emits an MCP_QUARANTINE_SKILL_MISSING warn
finding. The skill is emitted by every first-party adapter by default,
so the failure mode is an enabled untrusted server combined with a
hand-edited / Starter-minimal skill set.
Currently flagged servers in the catalog: context7, playwright,
chrome-devtools. New entries that fetch external content MUST set
the flag; new entries that operate only on local data (e.g. beads-mcp,
filesystem, postgres) leave it absent.
This is a documentary mitigation; the agent runtime must obey the skill. Strict-tier hook-enforced quarantine is post-v1.0 (see ADR-0019).
6. Supporting types
6.1 MCPInstallSpec
export interface MCPInstallSpec {
/** Shell command for adapters using CLI-based registration. */
command?: string;
/** Configuration block for adapters using config-file registration. */
configBlock?: {
/** Path of the config file relative to project root or user home. */
target: string;
/** Snippet to merge or insert. */
content: string;
/** How to integrate: append, merge, or replace section. */
mode: 'append' | 'merge' | 'replace-section';
};
/** Fallback instructions when neither command nor configBlock applies. */
manualInstructions?: string;
/** Whether the install operation requires network access. */
requiresNetwork: boolean;
/** Minimum target-CLI version required for this install method. */
minCliVersion?: string;
}
Rules:
- Exactly one of
command,configBlock, ormanualInstructionsMUST be present - If
commandis present, the adapter executes it as aShellCommand(seeadapter-contract.md) - If
configBlockis present, the adapter modifies the target config file accordingly manualInstructionsis the last resort; the CLI displays it and exits without installing
6.2 RoleName
Imported from @aidokit/core. The seven roles defined in adapter-contract.md.
6.3 InstalledMCP
Defined in adapter-contract.md.
7. Trigger grammar
Triggers are strings interpreted by a small predicate evaluator. They are NOT a full expression language.
7.1 Supported triggers
| Trigger | Evaluates to true when |
|---|---|
always |
Always (every project) |
never |
Never auto-suggested; explicit user add only |
beadsEnabled |
ctx.beadsEnabled === true |
brownfield |
ctx.brownfield === true |
stack.has(<id>) |
ctx.selectedStackPacks.includes(<id>) |
stack.hasFrontend |
Any selected pack declares languages including a frontend lang OR pack id matches known-frontend set |
stack.hasBackend |
Symmetric to stack.hasFrontend |
stack.hasDatabase |
Any selected pack declares a database in extras.databases |
stack.language(<lang>) |
Any selected pack declares <lang> in manifest.languages |
detect.githubRemote |
git remote -v shows a github.com origin |
detect.gitlabRemote |
git remote -v shows a gitlab.com origin |
detect.bitbucketRemote |
git remote -v shows a bitbucket.org origin |
os.darwin |
ctx.os === 'darwin' |
os.linux |
ctx.os === 'linux' |
os.win32 |
ctx.os === 'win32' |
conformance.minimum |
ctx.conformance === 'minimum' |
conformance.standard |
ctx.conformance === 'standard' |
conformance.strict |
ctx.conformance === 'strict' |
7.2 Multiple triggers
The triggers array is treated as logical OR. An MCP is suggested if any trigger evaluates to true.
For logical AND, multiple catalog entries with overlapping concerns are NOT the right model. Instead, define triggers conservatively and let users opt out.
7.3 never semantics
A trigger array of ['never'] means: do not auto-suggest under any condition. The MCP can still be installed via aido mcp add <id> if the user explicitly requests it (typically with a confirmation prompt due to securitySensitive: true).
7.4 Frontend / backend stack sets
The CLI maintains internal sets for stack.hasFrontend and stack.hasBackend:
const FRONTEND_STACKS = new Set([
'react',
'vue',
'svelte',
'angular',
'next-js',
'nuxt',
'remix',
'sveltekit',
'solid',
'qwik',
'astro',
]);
const BACKEND_STACKS = new Set([
'express',
'fastify',
'nest-js',
'koa',
'django',
'fastapi',
'flask',
'rails',
'sinatra',
'spring',
'go-gin',
'phoenix',
]);
These sets MAY grow as new stack packs ship.
7.5 Forbidden patterns
The trigger grammar MUST NOT support:
- Negation (no
not stack.has(react)) - Boolean operators (no
&&,||) - Arithmetic or comparison operators
- Custom JavaScript expressions
If a more complex condition is needed, it indicates the catalog entry should be split into multiple entries.
8. Install spec format
8.1 Command-based install (Claude Code)
install: {
'claude-code': {
command: 'claude mcp add context7 -- npx -y @upstash/context7-mcp',
requiresNetwork: true,
minCliVersion: '2.1.32',
},
}
The adapter wraps this in a ShellCommand:
{
command: 'claude mcp add context7 -- npx -y @upstash/context7-mcp',
description: 'Register Context7 MCP with Claude Code',
requiresConfirmation: false, // false because it's a CLI-side registration, not arbitrary shell
abortOnFailure: true,
}
8.2 Config-block install (Codex)
install: {
'codex': {
configBlock: {
target: '~/.codex/config.toml',
content: `
[mcp_servers.context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp"]
`.trim(),
mode: 'merge',
},
requiresNetwork: true,
},
}
The adapter merges this block into the user's Codex config, idempotently.
8.3 Manual-only install (rare)
install: {
'some-adapter': {
manualInstructions: 'Visit https://example.com/setup for installation steps.',
requiresNetwork: false,
},
}
The CLI displays the instructions and skips automatic install. Used only when neither command nor config-block applies.
8.4 Adapter-not-supported
If an adapter has no entry in the install record, the CLI MUST refuse to install the MCP on that adapter and report:
✘ MCP 'context7' has no install spec for adapter 'some-adapter'
Suggest opening an issue against the MCP catalog or the adapter.
9. Role scoping rules
9.1 Default scoping
When a user installs an MCP without specifying roles, the CLI MUST scope it to mcp.suggestedFor exactly.
9.2 User override
aido mcp add <id> --roles <list> overrides the default. The user MAY scope to any subset of valid roles, including roles outside suggestedFor (with a confirmation prompt).
9.3 Empty suggestedFor
For 'never'-triggered MCPs, suggestedFor MAY be empty. In that case, aido mcp add MUST prompt for roles; there is no default.
9.4 No "all roles" auto-suggestion
The CLI MUST NOT default-scope an MCP to all roles. Per-role MCP loading is a core design principle (every role getting every MCP causes context bloat and capability creep).
9.5 Adapter responsibility
The adapter implements the actual scoping (writing mcpServers: into agent frontmatter, for example). The catalog only declares which roles are appropriate; the adapter does the work.
10. Security model
10.1 The securitySensitive flag
An MCP MUST be marked securitySensitive: true when ANY of the following is true:
- It can write or delete files outside the engine directory
- It can execute arbitrary shell commands
- It can make outbound network calls with user-controlled URLs
- It accesses credentials, tokens, or secrets stored on the host
- It accesses cloud resources (AWS, GCP, Azure, etc.) with write privileges
- It can persist data beyond the current project's scope
10.2 Consequences of securitySensitive: true
| Consequence | Description |
|---|---|
| Not auto-suggested | Even if triggers match, sensitive MCPs are excluded from automatic suggestions during aidokit init |
| Confirmation on add | aido mcp add <id> MUST prompt for explicit confirmation, even with --yes (the --yes exception does not apply to security-sensitive MCPs) |
| Capability disclosure | The confirmation prompt MUST disclose what the MCP can do |
| Documentation requirement | The catalog entry's purpose field MUST mention the elevated capability |
10.3 Stack pack MUST NOT suggest sensitive MCPs
Per stack-pack-contract.md §12.3, suggestMCPs() MUST NOT return ids of MCPs marked securitySensitive: true.
10.4 No credential storage in catalog
The catalog MUST NOT include credentials, tokens, or environment-variable values in install specs. Any required credentials MUST be sourced from the user's environment at install time.
10.5 Audit log
Every MCP install MUST be recorded in <projectRoot>/.aido/state.json with:
{
"installedMCPs": [
{
"id": "context7",
"installedAt": "2026-05-12T14:30:00Z",
"scopedToRoles": ["researcher", "architect"],
"installCommand": "claude mcp add context7 -- npx -y @upstash/context7-mcp",
"userConfirmed": false
}
]
}
userConfirmed: true for securitySensitive installs; false otherwise.
11. The v1.0 catalog
The complete catalog shipping in @aidokit/mcp-catalog. The authoritative
source is packages/mcp-catalog/src/catalog.ts; the entries below mirror it.
11.1 context7
{
id: 'context7',
name: 'Context7',
purpose: 'Up-to-date library documentation lookup for agents.',
suggestedFor: ['researcher', 'architect'],
triggers: ['always'],
securitySensitive: false,
install: {
'claude-code': {
command: 'claude mcp add context7 -- npx -y @upstash/context7-mcp',
requiresNetwork: true,
minCliVersion: '2.1.32',
},
'codex': {
configBlock: {
target: '~/.codex/config.toml',
content: `
[mcp_servers.context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp"]
`.trim(),
mode: 'merge',
},
requiresNetwork: true,
},
},
homepage: 'https://github.com/upstash/context7',
tags: ['docs', 'research', 'libraries'],
}
11.2 beads-mcp
{
id: 'beads-mcp',
name: 'Beads MCP (cgb)',
purpose: 'Query the Beads task graph and decision memory from inside agent sessions.',
suggestedFor: ['planner', 'maintainer'],
triggers: ['beadsEnabled'],
securitySensitive: false,
install: {
'claude-code': {
command: 'claude mcp add beads -- bd mcp serve',
requiresNetwork: false,
minCliVersion: '2.1.32',
},
'codex': {
configBlock: {
target: '~/.codex/config.toml',
content: `
[mcp_servers.beads]
command = "bd"
args = ["mcp", "serve"]
`.trim(),
mode: 'merge',
},
requiresNetwork: false,
},
},
homepage: 'https://github.com/steveyegge/beads',
tags: ['tasks', 'memory', 'graph'],
}
11.3 playwright
{
id: 'playwright',
name: 'Playwright',
purpose: 'Browser automation for frontend test review and exploratory testing.',
suggestedFor: ['tester-reviewer', 'frontend-browser-tester'],
triggers: ['stack.hasFrontend'],
securitySensitive: false,
install: {
'claude-code': {
command: 'claude mcp add playwright -- npx -y @playwright/mcp@latest',
requiresNetwork: true,
minCliVersion: '2.1.32',
},
'codex': {
configBlock: {
target: '~/.codex/config.toml',
content: `
[mcp_servers.playwright]
command = "npx"
args = ["-y", "@playwright/mcp@latest"]
`.trim(),
mode: 'merge',
},
requiresNetwork: true,
},
},
homepage: 'https://github.com/microsoft/playwright-mcp',
tags: ['testing', 'browser', 'frontend'],
}
11.4 github (v0.5 addition)
{
id: 'github',
name: 'GitHub MCP',
purpose: 'Read-only access to GitHub repos, issues, and PRs. Write access opt-in.',
suggestedFor: ['researcher', 'planner'],
triggers: ['detect.githubRemote'],
securitySensitive: false, // read-only by default
install: {
'claude-code': {
command: 'claude mcp add github -- npx -y @modelcontextprotocol/server-github',
requiresNetwork: true,
minCliVersion: '2.1.32',
},
'codex': {
configBlock: {
target: '~/.codex/config.toml',
content: `
[mcp_servers.github]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
`.trim(),
mode: 'merge',
},
requiresNetwork: true,
},
},
homepage: 'https://github.com/modelcontextprotocol/servers/tree/main/src/github',
tags: ['repo', 'issues', 'pr'],
}
11.5 chrome-devtools (v0.5 addition)
{
id: 'chrome-devtools',
name: 'Chrome DevTools MCP',
purpose: 'Advanced browser debugging — DOM inspection, network analysis, performance traces.',
suggestedFor: ['frontend-browser-tester'],
triggers: ['stack.hasFrontend'],
securitySensitive: false,
install: {
'claude-code': {
command: 'claude mcp add chrome-devtools -- npx -y @benjaminr/chrome-devtools-mcp',
requiresNetwork: true,
minCliVersion: '2.1.32',
},
},
homepage: 'https://github.com/benjaminr/chrome-devtools-mcp',
tags: ['browser', 'debugging', 'frontend'],
}
11.6 filesystem (v0.5; security-sensitive)
{
id: 'filesystem',
name: 'Filesystem MCP',
purpose: 'Filesystem operations beyond the agent\'s default toolset. ⚠ Can write/delete files.',
suggestedFor: [], // empty — never auto-scoped
triggers: ['never'], // explicit opt-in only
securitySensitive: true,
install: {
'claude-code': {
command: 'claude mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem .',
requiresNetwork: true,
minCliVersion: '2.1.32',
},
},
homepage: 'https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem',
tags: ['filesystem', 'sensitive'],
}
11.7 postgres (v0.5; security-sensitive)
{
id: 'postgres',
name: 'Postgres MCP',
purpose: 'Natural-language SQL queries and schema inspection against a PostgreSQL database.',
suggestedFor: ['builder', 'architect'],
triggers: ['never'], // explicit opt-in only
securitySensitive: true,
install: {
'claude-code': {
command: 'claude mcp add postgres -- npx -y @modelcontextprotocol/server-postgres <connection-string>',
},
},
}
11.8 sentry (v1.0; security-sensitive)
{
id: 'sentry',
name: 'Sentry MCP',
purpose: 'Error tracking lookup, issue triage, and stack-trace inspection via the Sentry API.',
suggestedFor: ['builder', 'tester-reviewer', 'maintainer'],
triggers: ['never'],
securitySensitive: true,
install: {
'claude-code': {
command: 'claude mcp add sentry -e SENTRY_AUTH_TOKEN=<your-token> -- npx -y @sentry/mcp-server',
},
},
}
11.9 notion (v1.0; security-sensitive)
{
id: 'notion',
name: 'Notion MCP',
purpose: 'Read and write access to Notion pages and databases.',
suggestedFor: ['researcher', 'architect', 'planner'],
triggers: ['never'],
securitySensitive: true,
install: {
'claude-code': {
command: 'claude mcp add notion -e OPENAPI_MCP_HEADERS=\'{"Authorization":"Bearer <your-token>","Notion-Version":"2022-06-28"}\' -- npx -y @notionhq/notion-mcp-server',
},
},
}
11.10 linear (v1.0; security-sensitive)
{
id: 'linear',
name: 'Linear MCP',
purpose: 'Issue tracking, project management, and roadmap inspection via the Linear API.',
suggestedFor: ['researcher', 'planner', 'maintainer'],
triggers: ['never'],
securitySensitive: true,
install: {
'claude-code': {
command: 'claude mcp add --transport sse linear https://mcp.linear.app/sse',
},
},
}
11.11 graphify (v1.0-rc2; security-sensitive; ADR-0015 + ADR-0030)
Fills the v4 researcher agent's cgb slot. Code is parsed locally
(tree-sitter, no API calls); only non-code content (docs, PDFs, images, video
transcripts) is sent to the user's AI model. Requires Python on the host
(per-MCP prereqs; ADR-0008's no-auto-install applies).
Install command smoke-tested (ADR-0030 §3, verified 2026-06-09): the real
invocation is uvx --from "graphifyy[mcp]" graphify-mcp (equivalent to
upstream's python -m graphify.serve). The earlier uvx graphifyy mcp was
wrong — graphifyy is not an executable, there is no mcp subcommand, and the
server lives in the optional [mcp] extra. It serves over stdio and defaults
graph_path to graphify-out/graph.json (no path argument needed), but serves
a pre-built graph — the user must build one first (graphify's /graphify
skill or graphify .) or the server exits with "Graph file not found".
{
id: 'graphify',
name: 'graphify',
purpose: 'Code-graph / knowledge-graph MCP for dependency and impact analysis (fills the v4 researcher cgb slot). Code parsed locally; non-code content uses your AI model. Serves a pre-built graphify-out/graph.json — build the graph before enabling.',
suggestedFor: ['researcher', 'architect'],
triggers: ['never'],
securitySensitive: true,
install: {
'claude-code': {
command: 'claude mcp add graphify -- uvx --from "graphifyy[mcp]" graphify-mcp',
},
},
prereqs: ['python>=3.10', 'uv or pipx'],
}
11.12 Catalog summary
| ID | Since | Triggers | Sensitive | Suggested-for default |
|---|---|---|---|---|
context7 |
v0.1 | always |
❌ | researcher, architect |
beads-mcp |
v0.1 | beadsEnabled |
❌ | planner, maintainer |
playwright |
v0.1 | stack.hasFrontend |
❌ | tester-reviewer, frontend-browser-tester |
github |
v0.5 | detect.githubRemote |
✅ | researcher, planner, maintainer |
chrome-devtools |
v0.5 | stack.hasFrontend |
❌ | frontend-browser-tester, builder |
postgres |
v0.5 | never |
✅ | builder, architect |
filesystem |
v0.5 | never |
✅ | (empty — manual scoping) |
sentry |
v1.0 | never |
✅ | builder, tester-reviewer, maintainer |
notion |
v1.0 | never |
✅ | researcher, architect, planner |
linear |
v1.0 | never |
✅ | researcher, planner, maintainer |
graphify |
v1.0 | never |
✅ | researcher, architect (manual) |
githubissecuritySensitive: trueincatalog.ts(write access via PAT); the §11.4 snippet's oldersecuritySensitive: falsecomment predates that and is superseded by the code. The codexconfigBlockinstall is defined only for the v0.1/v0.5 entries;sentry/notion/linear/graphifyare claude-code only until the codex MCP surface stabilises.
12. Custom (non-catalog) MCPs
12.1 Why custom MCPs
Not every MCP a user wants will be in the catalog. The CLI MUST support adding arbitrary MCPs via aido mcp add <id> --custom-url <url>.
12.2 How custom MCPs work
When --custom-url <url> is provided:
- The CLI MUST prompt for explicit confirmation regardless of
--yes(treated as security-sensitive) - The CLI MUST disclose: install command, declared roles, source URL
- The CLI MUST mark the install in
state.jsonwithuserConfirmed: trueandsource: 'custom' - Custom MCPs are NOT subject to catalog versioning rules
- The CLI MAY emit a
MCP_NOT_IN_CATALOGwarning duringaidokit doctorfor custom-sourced MCPs
12.3 Custom MCP shape
{
id: '<user-provided>',
name: '<user-provided>',
purpose: '(custom)',
suggestedFor: [],
triggers: ['never'],
securitySensitive: true, // always treated as sensitive
install: {
'<current-adapter>': {
command: '<derived-from-url>',
requiresNetwork: true,
},
},
}
12.4 Limitations
Custom MCPs MUST NOT be auto-suggested. Custom MCPs MUST NOT be referenced by stack packs (which reference catalog ids only).
13. Catalog evolution
13.1 Adding entries
Adding a new MCP:
- Open an issue or PR against
@aidokit/mcp-catalog - Provide:
- The new
MCPDefentry following §5 - Justification for inclusion (use case, demand evidence)
- Test coverage (install commands work; trigger evaluation correct)
- The core team reviews against the checklist in Appendix B
- On acceptance, the entry is added in the next minor release of
@aidokit/mcp-catalog
13.2 Modifying entries
| Modification | Allowed | Process |
|---|---|---|
Renaming id |
❌ | Forbidden — id is permanent |
Updating name |
✅ | Minor patch |
Updating purpose |
✅ | Minor patch (significant rewording → minor bump) |
Adding to suggestedFor |
✅ | Minor bump |
Removing from suggestedFor |
⚠ | Minor bump; emit aidokit doctor warning for affected projects |
| Adding triggers | ✅ | Minor bump |
| Removing triggers | ⚠ | Minor bump; emit warning |
Updating install command |
✅ | Patch bump |
Adding adapter to install |
✅ | Minor bump |
Removing adapter from install |
⚠ | Minor bump; emit warning for affected projects |
Changing securitySensitive |
⚠ | Major bump (security implications) |
13.3 Deprecating entries
Entries are NEVER removed. To deprecate:
- Set
deprecated: true - Set
replacedBy: '<new-id>'if a replacement exists - The CLI excludes deprecated entries from
aido mcp suggest aidokit doctorwarns on projects with deprecated MCPs installed- Deprecated entries remain in the catalog indefinitely for compatibility
13.4 Removal exception
The ONLY case where an entry MAY be removed is if the upstream MCP server is taken offline AND no migration path exists. Even then, the entry SHOULD be marked deprecated for at least one minor version before removal.
14. Versioning
14.1 Catalog version = package version
@aidokit/mcp-catalog is independently versioned via semver. The catalog version is the package version.
14.2 Semver rules
- Major: Breaking changes to the
MCPDeftype, removal of any entry,securitySensitiveflips - Minor: New entries, new install adapters on existing entries,
deprecatedflag changes - Patch: Install command updates,
name/purpose/tagsrewording, bug fixes
14.3 Compatibility with aidokit CLI
The CLI declares a compatible catalog version range. Within that range:
- New entries are visible immediately (no CLI update needed)
- Deprecated entries surface warnings via
aidokit doctor
15. Worked example: lookup → suggest → install
15.1 Setup
User runs aidokit init in a project with:
package.jsonwithreactdependency- Git remote at
github.com:acme/myapp.git - Selects Claude Code adapter, Standard conformance, opts into Beads
15.2 ProjectContext built
{
selectedStackPacks: ['node-ts', 'react'],
adapterName: 'claude-code',
conformance: 'standard',
beadsEnabled: true,
os: 'darwin',
// ... other fields
}
15.3 Trigger evaluation
The CLI evaluates triggers for every catalog entry:
| MCP | Triggers | Eval result | Auto-suggested? |
|---|---|---|---|
context7 |
always |
✅ | yes |
beads-mcp |
beadsEnabled |
✅ | yes |
playwright |
stack.hasFrontend (react ✅) |
✅ | yes |
github |
detect.githubRemote |
✅ | yes |
chrome-devtools |
stack.hasFrontend (react ✅) |
✅ | yes |
filesystem |
never |
❌ | no (sensitive) |
15.4 Suggestion UI
🔌 Suggested MCP servers
? Select MCPs to install:
❯ ◉ Context7 → researcher, architect (docs lookup)
◉ Beads MCP → planner, maintainer (task graph)
◉ Playwright → tester-reviewer (frontend testing)
◉ GitHub MCP → researcher, planner (repo metadata)
◯ Chrome DevTools → frontend-browser-tester (advanced browser)
User accepts the first four; deselects Chrome DevTools.
15.5 Install
For each selected MCP, the CLI:
- Resolves the catalog entry
- Looks up
entry.install['claude-code'] - Calls
adapter.installMCP(entry, suggestedFor, ctx) - Executes returned
ShellCommands - Updates role frontmatter with
mcpServers: [...] - Appends to
state.json
15.6 Resulting state
$ aido mcp list
ID NAME SCOPED TO INSTALLED
context7 Context7 researcher, architect 2026-05-12
beads-mcp Beads MCP planner, maintainer 2026-05-12
playwright Playwright tester-reviewer 2026-05-12
github GitHub MCP researcher, planner 2026-05-12
16. Open questions
| Question | Resolution target |
|---|---|
Whether to add a requiredPrereqs field listing prereqs each MCP needs (e.g., GitHub MCP needs gh CLI? or just a token?) |
Spec 1.1 |
| Whether to support MCP-specific configuration (e.g., GitHub token via env var) in the catalog vs leave to user | Spec 1.0 freeze |
Whether to add a tested-against field per install spec (which CLI versions were verified) |
Spec 1.1 |
| How to surface MCP version compatibility (e.g., MCP server v2.x vs v3.x) in the catalog | Spec 1.1 |
Whether stack packs may include MCPs in tags that the catalog references back |
Spec 1.1 |
| How to handle MCPs that need user-specific credentials (e.g., AWS keys) | Spec 1.1 |
| Whether to support per-OS install variants (some MCPs install differently on Windows) | Spec 1.0 freeze |
Appendix A: Trigger evaluation reference
A.1 Pseudocode
function evaluateTrigger(trigger: string, ctx: ProjectContext): boolean {
if (trigger === 'always') return true;
if (trigger === 'never') return false;
if (trigger === 'beadsEnabled') return ctx.beadsEnabled;
if (trigger === 'brownfield') return ctx.brownfield;
if (trigger === 'stack.hasFrontend') {
return ctx.selectedStackPacks.some(id => FRONTEND_STACKS.has(id));
}
if (trigger === 'stack.hasBackend') {
return ctx.selectedStackPacks.some(id => BACKEND_STACKS.has(id));
}
if (trigger === 'stack.hasDatabase') {
return ctx.selectedStackPacks.some(id => /* check pack's extras */);
}
let m = trigger.match(/^stack\.has\((.+)\)$/);
if (m) return ctx.selectedStackPacks.includes(m[1]);
m = trigger.match(/^stack\.language\((.+)\)$/);
if (m) return ctx.stack.languages.includes(m[1]);
if (trigger === 'detect.githubRemote') return ctx.gitRemote?.includes('github.com') ?? false;
if (trigger === 'detect.gitlabRemote') return ctx.gitRemote?.includes('gitlab.com') ?? false;
if (trigger === 'os.darwin') return ctx.os === 'darwin';
if (trigger === 'os.linux') return ctx.os === 'linux';
if (trigger === 'os.win32') return ctx.os === 'win32';
if (trigger.startsWith('conformance.')) {
return ctx.conformance === trigger.split('.')[1];
}
// Unknown trigger — fail closed
console.warn(`Unknown trigger: ${trigger}`);
return false;
}
function shouldSuggest(mcp: MCPDef, ctx: ProjectContext): boolean {
if (mcp.deprecated) return false;
if (mcp.securitySensitive) return false;
return mcp.triggers.some(t => evaluateTrigger(t, ctx));
}
A.2 Test cases
| Trigger | Context | Expected |
|---|---|---|
always |
any | true |
never |
any | false |
stack.has(react) |
selectedStackPacks: ['react'] |
true |
stack.has(react) |
selectedStackPacks: ['vue'] |
false |
stack.hasFrontend |
selectedStackPacks: ['react'] |
true |
stack.hasFrontend |
selectedStackPacks: ['node-ts'] |
false |
beadsEnabled |
beadsEnabled: true |
true |
detect.githubRemote |
gitRemote: 'https://github.com/acme/myapp.git' |
true |
os.darwin |
os: 'linux' |
false |
conformance.strict |
conformance: 'standard' |
false |
unknown.trigger |
any | false (warns) |
Appendix B: Adding a new catalog entry
Checklist for proposing a new MCP entry:
- [ ] Run the MCP server locally; verify it works
- [ ] Identify the appropriate
suggestedForroles (default scoping, not "all") - [ ] Determine triggers (NOT
'always'unless universally useful) - [ ] Decide
securitySensitiveflag honestly - [ ] Write install specs for at least one adapter (more if MCP supports them)
- [ ] Provide
homepageURL - [ ] Add appropriate
tagsfor marketplace browsing - [ ] Ensure
idis kebab-case and not already used - [ ] Run trigger evaluation tests (Appendix A.2 cases) for the new entry
- [ ] Update
MCP_CATALOGarray in@aidokit/mcp-catalog/src/catalog.ts - [ ] Add entry to §11 of this spec
- [ ] Bump
@aidokit/mcp-catalogminor version - [ ] Document in CHANGELOG
See also
adapter-contract.md— Adapter interface consuminginstallspecsstack-pack-contract.md— Stack-pack interface referencing catalog idscli-reference.md—aidokit mcpcommand surfaceconformance-levels.md— Conformance requirements../../ARCHITECTURE.md— MCP catalog model in system context- Upstream: https://modelcontextprotocol.io — MCP protocol specification