Skip to content

mcp.projection

Who answers `initialize` and with which protocol versions; the transports and the sessions attached over each; the methods served, and that prompts and notifications are not; every tool (or only those with one effect) with its capability, effect and the hints its annotations are derived from; the tools that write the repository; the resources; and whether each client the distribution declares has a configuration here that starts this repository's server. Reads the registry and one file per declared client; writes nothing.

Who answers `initialize` and with which protocol versions; the transports and the sessions attached over each; the methods served, and that prompts and notifications are not; every tool (or only those with one effect) with its capability, effect and the hints its annotations are derived from; the tools that write the repository; the resources; and whether each client the distribution declares has a configuration here that starts this repository's server. Reads the registry and one file per declared client; writes nothing.

  • query
  • experimental
  • module mcp
  • #mcp
  • #introspection
  • #projections

Exposure

Where this one definition is reachable. Absence is explicit: a surface not listed does not carry it.

surfaceas
MCPtool majordomus_mcp
resource majordomus://mcp
HTTPGET /api/v1/mcp operationId mcp.projection
command line—

Schemas

The canonical input and output, from the Rust types; the MCP tool schema and the OpenAPI parameters and responses are derived from these.

input · McpProjectionInput

The input of mcp.projection: which tools to list. Everything else in the answer is about the projection as a whole and is always given.

use majordomus_cli::capability::builtin::mcp::McpProjectionInput;
use majordomus_cli::capability::Effect;
let all: McpProjectionInput = serde_json::from_str("{}").unwrap();
assert!(all.effect.is_none());
let writers: McpProjectionInput =
    serde_json::from_str(r#"{"effect":"repository_mutation"}"#).unwrap();
assert_eq!(writers.effect, Some(Effect::RepositoryMutation));
propertytyperequireddescription
effect one of 2 no List only the tools with this effect: read, process_state or repository_mutation. Absent lists every tool.
JSON Schema
{
  "$defs": {
    "ExecutionEffect": {
      "description": "What running a capability changes outside the caller.\n\nClassified, never declared: it follows from the kind, which is the field a declaration\nalready carries. A projection reads this to decide whether to ask before running\nsomething — the Cockpit's confirmation is derived from it — instead of naming\ncapabilities it must treat carefully, which is a list that goes stale the day after it\nis written.",
      "oneOf": [
        {
          "const": "read",
          "description": "Nothing changes. Every query and every resource of this executable.",
          "type": "string"
        },
        {
          "const": "process_state",
          "description": "This process's own memory changes, and nothing outside it.",
          "type": "string"
        },
        {
          "const": "repository_mutation",
          "description": "The repository changes.\n\nFor most of this executable's life nothing classified to this, because no capability\nof any kind wrote to the repository. That day arrived with [ADR 0040]: development\nsemantics are capabilities of this runtime, and the first of them — `plan.transition`\n— stamps a field into an issue's own tracked record. The variant was put on the model\nin advance so that when one did, it would say so *here*, where a projection already\nreads it and a client already asks before running it, rather than in whichever page\nhappens to render its button.\n\nIt is not classified from the kind, because the kind cannot know it: a command that\nannounces a peer and a command that writes a tracked file are the same kind. A\nhandler that writes the repository says so with\n`ExecutionPolicy::writes_repository`, the way one that stops says so with\n`ExecutionPolicy::stoppable`.\n\n[ADR 0040]: https://github.com/korczis/prismatic-majordomus/blob/master/.ai/repo/adrs/0040-development-semantics-are-capabilities-of-one-runtime.md",
          "type": "string"
        }
      ]
    }
  },
  "additionalProperties": false,
  "description": "The input of `mcp.projection`: which tools to list. Everything else in the answer is\nabout the projection as a whole and is always given.\n\n```\nuse majordomus_cli::capability::builtin::mcp::McpProjectionInput;\nuse majordomus_cli::capability::Effect;\nlet all: McpProjectionInput = serde_json::from_str(\"{}\").unwrap();\nassert!(all.effect.is_none());\nlet writers: McpProjectionInput =\n    serde_json::from_str(r#\"{\"effect\":\"repository_mutation\"}\"#).unwrap();\nassert_eq!(writers.effect, Some(Effect::RepositoryMutation));\n```",
  "properties": {
    "effect": {
      "anyOf": [
        {
          "$ref": "#/$defs/ExecutionEffect"
        },
        {
          "type": "null"
        }
      ],
      "description": "List only the tools with this effect: `read`, `process_state` or\n`repository_mutation`. Absent lists every tool."
    }
  },
  "title": "McpProjectionInput",
  "type": "object"
}

output · McpProjection

The MCP projection of this registry in this repository.

use majordomus_cli::capability::builtin::mcp::McpProjection;
let p: McpProjection = serde_json::from_value(serde_json::json!({
    "server": { "name": "majordomus", "title": "Majordomus", "version": "0.14.0" },
    "protocol_versions": ["2025-06-18"],
    "transports": [{ "id": "stdio", "reached_by": "majordomus mcp", "attached": 0 }],
    "serving": { "methods": ["initialize"], "tools": true, "resources": true, "prompts": false, "notifications": false },
    "tool_count": 2,
    "tools": [],
    "effects": [{ "effect": "read", "tools": 1 }, { "effect": "repository_mutation", "tools": 1 }],
    "writers": ["majordomus_plan_transition"],
    "resources": { "builtin": 1, "objects": 0, "template": "majordomus://<kind>/<identity>" },
    "launcher_in_repository": false,
    "clients": [],
    "findings": [],
})).unwrap();
// the count is of every tool, whatever the `effect` filter narrowed `tools` to
assert_eq!(p.tool_count, p.effects.iter().map(|e| e.tools).sum::<usize>());
assert!(p.tools.is_empty() && p.writers.len() == 1);
propertytyperequireddescription
clients array yes The clients the distribution declares a configuration for, and where each stands.
effects array yes The tools counted by effect, every effect present.
findings array yes What a person should act on. Empty is nothing to do.
launcher_in_repository boolean yes Whether this repository carries the launcher at bin/majordomus-mcp. Where it does not, the installed command of the same name is what a configuration names.
protocol_versions array yes The protocol versions accepted, newest first.
resources reference yes The resources.
server reference yes Who answers initialize.
serving reference yes What of the protocol is served.
tool_count integer yes How many tools the registry projects, whatever the filter asked for.
tools array yes The tools asked for, in registry order.
transports array yes How a client reaches the server.
writers array yes The tools that write the repository, by name. The initialize instructions name the same ones.
JSON Schema
{
  "$defs": {
    "ExecutionEffect": {
      "description": "What running a capability changes outside the caller.\n\nClassified, never declared: it follows from the kind, which is the field a declaration\nalready carries. A projection reads this to decide whether to ask before running\nsomething — the Cockpit's confirmation is derived from it — instead of naming\ncapabilities it must treat carefully, which is a list that goes stale the day after it\nis written.",
      "oneOf": [
        {
          "const": "read",
          "description": "Nothing changes. Every query and every resource of this executable.",
          "type": "string"
        },
        {
          "const": "process_state",
          "description": "This process's own memory changes, and nothing outside it.",
          "type": "string"
        },
        {
          "const": "repository_mutation",
          "description": "The repository changes.\n\nFor most of this executable's life nothing classified to this, because no capability\nof any kind wrote to the repository. That day arrived with [ADR 0040]: development\nsemantics are capabilities of this runtime, and the first of them — `plan.transition`\n— stamps a field into an issue's own tracked record. The variant was put on the model\nin advance so that when one did, it would say so *here*, where a projection already\nreads it and a client already asks before running it, rather than in whichever page\nhappens to render its button.\n\nIt is not classified from the kind, because the kind cannot know it: a command that\nannounces a peer and a command that writes a tracked file are the same kind. A\nhandler that writes the repository says so with\n`ExecutionPolicy::writes_repository`, the way one that stops says so with\n`ExecutionPolicy::stoppable`.\n\n[ADR 0040]: https://github.com/korczis/prismatic-majordomus/blob/master/.ai/repo/adrs/0040-development-semantics-are-capabilities-of-one-runtime.md",
          "type": "string"
        }
      ]
    },
    "ExecutionHints": {
      "description": "What a caller may assume about a call before making it: the four facts MCP names tool\nannotations, held once on the model so that no projection states its own.\n\nClassified by `ExecutionPolicy::hints`, never declared.\n\n```\nuse majordomus_cli::capability::{CapabilityKind, ExecutionPolicy, Hints};\nlet read: Hints = ExecutionPolicy::classify(CapabilityKind::Query).hints();\nassert_eq!(\n    serde_json::to_value(read).unwrap(),\n    serde_json::json!({ \"read_only\": true, \"destructive\": false, \"idempotent\": true, \"open_world\": false })\n);\n```",
      "properties": {
        "destructive": {
          "description": "The call may overwrite or remove something that outlives this process.",
          "type": "boolean"
        },
        "idempotent": {
          "description": "A second identical call changes nothing the first did not.",
          "type": "boolean"
        },
        "open_world": {
          "description": "The call reaches beyond this machine. No handler of this executable does: observing\nthe forge and talking to an advisor are commands a person runs, not capabilities.",
          "type": "boolean"
        },
        "read_only": {
          "description": "The call changes nothing, anywhere.",
          "type": "boolean"
        }
      },
      "required": [
        "read_only",
        "destructive",
        "idempotent",
        "open_world"
      ],
      "type": "object"
    },
    "McpClient": {
      "description": "One client the distribution declares a project-scoped configuration for.\n\n```\nuse majordomus_cli::capability::builtin::mcp::{McpClient, McpClientStanding};\nlet mut clients: Vec<McpClient> = serde_json::from_value(serde_json::json!([\n    { \"id\": \"gemini\", \"title\": \"Gemini CLI\", \"config\": \".gemini/settings.json\", \"standing\": \"absent\" },\n    { \"id\": \"claude-code\", \"title\": \"Claude Code\", \"config\": \".mcp.json\", \"standing\": \"wired\" },\n])).unwrap();\n// ordered by the provider's id, whatever order the distribution declared them in\nmajordomus_cli::order::canonical(&mut clients);\nassert_eq!(clients[0].id, \"claude-code\");\nassert_eq!(clients[0].standing, McpClientStanding::Wired);\n```",
      "properties": {
        "config": {
          "description": "The file it reads, repository-relative.",
          "type": "string"
        },
        "id": {
          "description": "The provider id.",
          "type": "string"
        },
        "standing": {
          "$ref": "#/$defs/McpClientStanding",
          "description": "Where that file stands here."
        },
        "title": {
          "description": "The name a person knows it by.",
          "type": "string"
        }
      },
      "required": [
        "id",
        "title",
        "config",
        "standing"
      ],
      "type": "object"
    },
    "McpClientStanding": {
      "description": "Where a client's project-scoped configuration stands in this repository.\n\n```\nuse majordomus_cli::capability::builtin::mcp::McpClientStanding;\n// the words a caller reads, as they are written on every surface\nassert_eq!(serde_json::to_value(McpClientStanding::Wired).unwrap(), \"wired\");\nassert_eq!(serde_json::to_value(McpClientStanding::Foreign).unwrap(), \"foreign\");\nassert_eq!(serde_json::to_value(McpClientStanding::Absent).unwrap(), \"absent\");\n```",
      "oneOf": [
        {
          "const": "wired",
          "description": "The file exists and names the launcher: this client starts this repository's server.",
          "type": "string"
        },
        {
          "const": "foreign",
          "description": "The file exists and does not name the launcher: the client reads it and starts\nsomething else, or nothing.",
          "type": "string"
        },
        {
          "const": "absent",
          "description": "The file does not exist: this client starts no server here until it is written.",
          "type": "string"
        }
      ]
    },
    "McpEffectCount": {
      "description": "How many tools have one effect.\n\n```\nuse majordomus_cli::capability::builtin::mcp::McpEffectCount;\nuse majordomus_cli::capability::Effect;\nlet c: McpEffectCount =\n    serde_json::from_value(serde_json::json!({ \"effect\": \"read\", \"tools\": 149 })).unwrap();\nassert_eq!((c.effect, c.tools), (Effect::Read, 149));\n```",
      "properties": {
        "effect": {
          "$ref": "#/$defs/ExecutionEffect",
          "description": "The effect."
        },
        "tools": {
          "description": "How many tools have it.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "effect",
        "tools"
      ],
      "type": "object"
    },
    "McpFinding": {
      "description": "Something about the projection a person should act on.\n\n```\nuse majordomus_cli::capability::builtin::mcp::McpFinding;\nlet f: McpFinding = serde_json::from_value(serde_json::json!({\n    \"code\": \"mcp_client_config_foreign\",\n    \"message\": \".mcp.json exists and does not name majordomus-mcp\",\n    \"remedy\": \"add a `majordomus` server to .mcp.json whose command is majordomus-mcp\",\n})).unwrap();\n// a finding always says what to do, not only what is wrong\nassert!(!f.code.is_empty() && !f.remedy.is_empty());\n```",
      "properties": {
        "code": {
          "description": "The stable code.",
          "type": "string"
        },
        "message": {
          "description": "What is wrong.",
          "type": "string"
        },
        "remedy": {
          "description": "What to do about it.",
          "type": "string"
        }
      },
      "required": [
        "code",
        "message",
        "remedy"
      ],
      "type": "object"
    },
    "McpResourceSummary": {
      "description": "The resources a client can read.\n\n```\nuse majordomus_cli::capability::builtin::mcp::McpResourceSummary;\nlet r: McpResourceSummary = serde_json::from_value(serde_json::json!({\n    \"builtin\": 54, \"objects\": 1742, \"template\": \"majordomus://<kind>/<identity>\",\n})).unwrap();\n// the two counts together are the length of `resources/list`\nassert_eq!(r.builtin + r.objects, 1796);\n```",
      "properties": {
        "builtin": {
          "description": "Resources a builtin capability answers.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "objects": {
          "description": "Objects of this repository's layer, each one more resource.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "template": {
          "description": "The URI shape of an object's resource.",
          "type": "string"
        }
      },
      "required": [
        "builtin",
        "objects",
        "template"
      ],
      "type": "object"
    },
    "McpServerIdentity": {
      "description": "Who answers `initialize`, as it answers.\n\n```\nuse majordomus_cli::capability::builtin::mcp::McpServerIdentity;\nlet s: McpServerIdentity = serde_json::from_value(serde_json::json!({\n    \"name\": \"majordomus\", \"title\": \"Majordomus\", \"version\": \"0.14.0\",\n})).unwrap();\n// the version is the executable's: the protocol adapter has none of its own to drift\nassert_eq!((s.name.as_str(), s.version.as_str()), (\"majordomus\", \"0.14.0\"));\n```",
      "properties": {
        "name": {
          "description": "The server name.",
          "type": "string"
        },
        "title": {
          "description": "The name a person reads.",
          "type": "string"
        },
        "version": {
          "description": "The version: this executable's, never one of the protocol adapter's own.",
          "type": "string"
        }
      },
      "required": [
        "name",
        "title",
        "version"
      ],
      "type": "object"
    },
    "McpServing": {
      "description": "What the server serves of the protocol, and what it does not.\n\n```\nuse majordomus_cli::capability::builtin::mcp::McpServing;\nlet s: McpServing = serde_json::from_value(serde_json::json!({\n    \"methods\": [\"initialize\", \"tools/call\"], \"tools\": true, \"resources\": true,\n    \"prompts\": false, \"notifications\": false,\n})).unwrap();\n// what is not served is stated, so a client does not learn it from a refusal\nassert!(s.tools && !s.prompts && !s.notifications);\nassert!(!s.methods.iter().any(|m| m.starts_with(\"prompts/\")));\n```",
      "properties": {
        "methods": {
          "description": "The methods a request may name. Anything else is answered `-32601`.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "notifications": {
          "description": "Subscriptions and list-change notifications. Not served: this server sends nothing\nunasked.",
          "type": "boolean"
        },
        "prompts": {
          "description": "Prompts. Not served: the repository's prompt assets are resources, because a prompt\nserved unrendered would read as finished.",
          "type": "boolean"
        },
        "resources": {
          "description": "Resources: every capability that declares one, and every object of the layer.",
          "type": "boolean"
        },
        "tools": {
          "description": "Tools: every capability that declares one.",
          "type": "boolean"
        }
      },
      "required": [
        "methods",
        "tools",
        "resources",
        "prompts",
        "notifications"
      ],
      "type": "object"
    },
    "McpToolEntry": {
      "description": "One tool, as the registry projects it.\n\n```\nuse majordomus_cli::capability::builtin::mcp::McpToolEntry;\nuse majordomus_cli::capability::Effect;\nlet t: McpToolEntry = serde_json::from_value(serde_json::json!({\n    \"name\": \"majordomus_plan_transition\", \"capability\": \"plan.transition\",\n    \"module\": \"plan\", \"title\": \"Move an issue\", \"effect\": \"repository_mutation\",\n    \"hints\": { \"read_only\": false, \"destructive\": true, \"idempotent\": false, \"open_world\": false },\n})).unwrap();\nassert_eq!(t.effect, Effect::RepositoryMutation);\n// the hints are the effect's classification, carried beside it rather than restated\nassert!(t.hints.destructive && !t.hints.read_only && !t.hints.idempotent);\n```",
      "properties": {
        "capability": {
          "description": "The capability it projects; `capabilities.describe` answers its schemas.",
          "type": "string"
        },
        "effect": {
          "$ref": "#/$defs/ExecutionEffect",
          "description": "What a call changes."
        },
        "hints": {
          "$ref": "#/$defs/ExecutionHints",
          "description": "What a caller may assume before calling; the tool's annotations are this value."
        },
        "module": {
          "description": "The module that composes the capability.",
          "type": "string"
        },
        "name": {
          "description": "The name a client calls.",
          "type": "string"
        },
        "title": {
          "description": "The title.",
          "type": "string"
        }
      },
      "required": [
        "name",
        "capability",
        "module",
        "title",
        "effect",
        "hints"
      ],
      "type": "object"
    },
    "McpTransportEntry": {
      "description": "One way a client reaches the server.\n\n```\nuse majordomus_cli::capability::builtin::mcp::McpTransportEntry;\nlet t: McpTransportEntry = serde_json::from_value(serde_json::json!({\n    \"id\": \"stdio\", \"reached_by\": \"majordomus mcp\", \"attached\": 1,\n})).unwrap();\n// a count of sessions at the moment of asking, not a claim that a server is running\nassert_eq!((t.id.as_str(), t.attached), (\"stdio\", 1));\n```",
      "properties": {
        "attached": {
          "description": "Sessions attached to this process over it right now.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "id": {
          "description": "`stdio` or `http`.",
          "type": "string"
        },
        "reached_by": {
          "description": "How it is reached: the command a client starts, or the path on the shared server.",
          "type": "string"
        }
      },
      "required": [
        "id",
        "reached_by",
        "attached"
      ],
      "type": "object"
    }
  },
  "description": "The MCP projection of this registry in this repository.\n\n```\nuse majordomus_cli::capability::builtin::mcp::McpProjection;\nlet p: McpProjection = serde_json::from_value(serde_json::json!({\n    \"server\": { \"name\": \"majordomus\", \"title\": \"Majordomus\", \"version\": \"0.14.0\" },\n    \"protocol_versions\": [\"2025-06-18\"],\n    \"transports\": [{ \"id\": \"stdio\", \"reached_by\": \"majordomus mcp\", \"attached\": 0 }],\n    \"serving\": { \"methods\": [\"initialize\"], \"tools\": true, \"resources\": true, \"prompts\": false, \"notifications\": false },\n    \"tool_count\": 2,\n    \"tools\": [],\n    \"effects\": [{ \"effect\": \"read\", \"tools\": 1 }, { \"effect\": \"repository_mutation\", \"tools\": 1 }],\n    \"writers\": [\"majordomus_plan_transition\"],\n    \"resources\": { \"builtin\": 1, \"objects\": 0, \"template\": \"majordomus://<kind>/<identity>\" },\n    \"launcher_in_repository\": false,\n    \"clients\": [],\n    \"findings\": [],\n})).unwrap();\n// the count is of every tool, whatever the `effect` filter narrowed `tools` to\nassert_eq!(p.tool_count, p.effects.iter().map(|e| e.tools).sum::<usize>());\nassert!(p.tools.is_empty() && p.writers.len() == 1);\n```",
  "properties": {
    "clients": {
      "description": "The clients the distribution declares a configuration for, and where each stands.",
      "items": {
        "$ref": "#/$defs/McpClient"
      },
      "type": "array"
    },
    "effects": {
      "description": "The tools counted by effect, every effect present.",
      "items": {
        "$ref": "#/$defs/McpEffectCount"
      },
      "type": "array"
    },
    "findings": {
      "description": "What a person should act on. Empty is nothing to do.",
      "items": {
        "$ref": "#/$defs/McpFinding"
      },
      "type": "array"
    },
    "launcher_in_repository": {
      "description": "Whether this repository carries the launcher at `bin/majordomus-mcp`. Where it does\nnot, the installed command of the same name is what a configuration names.",
      "type": "boolean"
    },
    "protocol_versions": {
      "description": "The protocol versions accepted, newest first.",
      "items": {
        "type": "string"
      },
      "type": "array"
    },
    "resources": {
      "$ref": "#/$defs/McpResourceSummary",
      "description": "The resources."
    },
    "server": {
      "$ref": "#/$defs/McpServerIdentity",
      "description": "Who answers `initialize`."
    },
    "serving": {
      "$ref": "#/$defs/McpServing",
      "description": "What of the protocol is served."
    },
    "tool_count": {
      "description": "How many tools the registry projects, whatever the filter asked for.",
      "format": "uint",
      "minimum": 0,
      "type": "integer"
    },
    "tools": {
      "description": "The tools asked for, in registry order.",
      "items": {
        "$ref": "#/$defs/McpToolEntry"
      },
      "type": "array"
    },
    "transports": {
      "description": "How a client reaches the server.",
      "items": {
        "$ref": "#/$defs/McpTransportEntry"
      },
      "type": "array"
    },
    "writers": {
      "description": "The tools that write the repository, by name. The `initialize` instructions name the\nsame ones.",
      "items": {
        "type": "string"
      },
      "type": "array"
    }
  },
  "required": [
    "server",
    "protocol_versions",
    "transports",
    "serving",
    "tool_count",
    "tools",
    "effects",
    "writers",
    "resources",
    "launcher_in_repository",
    "clients",
    "findings"
  ],
  "title": "McpProjection",
  "type": "object"
}

Policies

benchmark
required — a target on every transport the exposure declares; the cases are the input type's
cache
disabled — every call runs the handler

Benchmark targets

Derived from the registry for this repository: one requirement per transport, and the cases the input type provides. The whole matrix is on the benchmarks page.

transportstatecasestargets
directcovered2every-tool
writers
mcpcovered2every-tool
writers
httpcovered2every-tool
writers

Searches the titles and descriptions of every page this site publishes.