Skip to content

MCP rule authoring

Drive CodeCharter from an AI assistant via the MCP server and the AI-assisted rule-authoring loop.

CodeCharter ships an MCP server so an AI coding assistant can analyze your code and help you write rules without leaving the chat. The assistant calls a small set of tools; you stay in control of what gets saved.

Installing the server in your AI tool

Prerequisites: the CodeCharter CLI must be installed and on your PATH, and a valid license is required — both codecharter mcp and codecharter mcp install exit with a license error otherwise. The install command only writes the client's config file; the AI tool itself is needed to actually use the server.

codecharter mcp install --client <claude-code|claude-desktop|cursor|windsurf|gemini>

By default the command writes the CodeCharter MCP server into a project-level config file in the current directory (for example .mcp.json for Claude Code); pass --scope user for the user-level config instead. Claude Desktop and Windsurf only support user scope, so pass --scope user for them. Use --dry-run to print the resulting diff without writing anything, and --force to overwrite an existing CodeCharter entry. Run the command once per tool you use:

codecharter mcp install --client claude-code
codecharter mcp install --client cursor

Supported --client values are claude-code, claude-desktop, cursor, windsurf, and gemini (Google's Gemini CLI). The Windows installer can register the server for the tools it detects, so on Windows you usually do not need to run this by hand.

For an MCP client that is not in the list, configure it to run codecharter mcp, which starts the server on stdio. An HTTP transport is also available; see the next section.

HTTP transport

For clients that connect over HTTP instead of stdio, start the server with --transport http:

codecharter mcp --transport http

By default the server listens on http://127.0.0.1:7777/mcp and prints the listening address to stderr. It exposes two endpoints:

  • POST /mcp — the client sends JSON-RPC requests here.
  • GET /mcp — a server-sent-events (SSE) stream for server-to-client notifications.
Flag Default Purpose
--port 7777 TCP port to listen on.
--bind 127.0.0.1 IP address to bind to.
--token auto-generated Bearer token (literal value).
--token-file File whose content is used as the Bearer token.
--allow-public off Required to bind a non-loopback address.
--cors-origin same-origin only CORS origin to whitelist; can be repeated.

Authentication

Bearer-token authentication is mandatory: every request must carry an Authorization: Bearer <token> header. A missing or wrong token gets a 401 Unauthorized response. Provide the token either literally with --token or via --token-file, whose file content (trimmed of whitespace) becomes the token. If you pass neither, the server generates a random token at startup and prints it once to stderr:

[codecharter mcp] Bearer token: <generated-token>

Network exposure

The default bind address 127.0.0.1 keeps the server reachable from the local machine only; localhost and other loopback addresses work the same way. Binding to a non-loopback address is refused unless you also pass --allow-public — a deliberate safety latch so the server is never exposed to the network by accident. With --allow-public the server starts and prints a security warning to stderr.

The server speaks plain http only; there is no built-in TLS. If you need to reach it from beyond the local machine, put a TLS-terminating reverse proxy in front of it and keep the token secret.

CORS

By default no cross-origin browser requests are allowed (same-origin only). Pass --cors-origin once per origin you want to whitelist, for example --cors-origin https://app.example.com.

Example

Start the server with a fixed token:

codecharter mcp --transport http --port 8080 --token my-secret-token

Then point your MCP client at http://127.0.0.1:8080/mcp with the header Authorization: Bearer my-secret-token. To verify the connection by hand:

curl -X POST http://127.0.0.1:8080/mcp \
  -H "Authorization: Bearer my-secret-token" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

The tools

The server exposes two groups of tools: analysis tools that run rules against real code, and authoring tools that help you draft and verify a new rule. Alongside the tools it also exposes MCP resources — docs://getting-started, docs://dsl-grammar, docs://predicates, docs://rule-examples, and rules://catalog — for clients that surface resources.

Analysis

Tool What it does
analyze_solution Run the active rule set against a whole .sln / .slnx / .csproj and return the findings.
analyze_file Run the rules against a single file. Fast feedback while editing.
analyze_diff Run the rules against a diff (e.g. the staged changes), so the assistant sees only what changed.

Every finding carries the rule's recommendation alongside the message, so the assistant can propose a fix without a second explain_rule call.

All three analysis tools accept a min_severity parameter that filters the findings: one of info, warn, or error — note the spelling warn, not warning; findings also report their severity as warn. When omitted, all severities are returned.

Per-tool notes:

  • analyze_solution also accepts include_paths and exclude_paths, each a list of glob patterns such as src/**/*.cs or tests/**. When include_paths is omitted, all files are included; exclusions are applied after inclusions. While iterating the source files the tool sends MCP progress notifications, so a client can show progress on large solutions. If the client cancels the call, the tool returns the findings collected so far as a partial result with its isCancelled flag set.
  • analyze_diff takes the changes as exactly one of two parameters: diff (raw unified diff text, e.g. the output of git diff) or git_ref (a ref range such as main..HEAD, which the server resolves by running git diff itself). Passing both, or neither, is an error.
  • analyze_diff with include_context: true additionally returns findings in diff-touched files whose reported line lies outside the changed lines, for example a missing-documentation finding on a newly added method when the diagnostic points at a line outside the changed range. These indirect findings arrive in a separate contextFindings collection, never overlap with the direct findings, and respect min_severity. The default (include_context: false) keeps the previous behavior.

Authoring

Tool What it does
scaffold_rule Turn a one-line description into a ready-to-edit .cgr skeleton, with the frontmatter filled in and the closest worked example to adapt.
get_authoring_docs Fetch the DSL grammar, predicate catalog, or worked examples as text, so the assistant drafts against the real reference instead of guessing.
list_rules List the rules currently in scope, with their ids and severities.
explain_rule Explain what a given rule matches and why, in plain language. On an unknown rule_id it answers with did-you-mean suggestions listing the closest known rule ids.
validate_rule Parse a draft rule and check its frontmatter for completeness. Parse errors include a hint pointing at the matching get_authoring_docs topic, and a thin frontmatter surfaces as warnings. The equivalent of codecharter validate.
dry_run_rule Run a draft rule against real code without saving it, to see what it would flag. Returns up to 50 matches by default (raise max_results for more) and flags when the result is truncated. Accepts the same include_paths / exclude_paths globs as analyze_solution, and severity_override (info, warn, error) replaces the rule's declared @severity for this run only.
test_rule_spec Run a draft rule against a .spec.md of hits and misses. The equivalent of codecharter test.
save_rule Write a validated draft into your workspace ./rules directory. Refuses a broken draft, and will not overwrite an existing rule unless you pass overwrite=true. The file name comes from rule_id; when omitted it defaults to a slug derived from the rule's @name directive (lowercase, hyphen-separated).

The authoring loop

Writing a rule typically follows this sequence:

  1. Scaffoldscaffold_rule turns a one-line description into a .cgr skeleton with the frontmatter filled in.
  2. Validatevalidate_rule checks that the draft parses and that the frontmatter is complete. Syntax mistakes surface here with a hint pointing at the matching get_authoring_docs topic.
  3. Dry-rundry_run_rule runs the draft against your real code without saving it, so you can see over- and under-matching before committing.
  4. Spectest_rule_spec runs the draft against a .spec.md of explicit hits and misses. A passing spec means the rule does what you intended.
  5. Savesave_rule writes the validated draft to the ./rules directory in your workspace, where codecharter analyze picks it up. A broken draft is rejected.

All steps before save_rule are in-memory. Nothing touches your rule set until you explicitly save. Use get_authoring_docs at any point to pull the DSL grammar or predicate catalog into the conversation.

Which rules the MCP server sees

The server resolves its rules directory in the same order as codecharter analyze: an explicitly given rules path wins, then a rules folder in the workspace, then the rules bundled next to the codecharter binary. codecharter mcp has no flag for an explicit path, so in practice the analysis tools use the rules folder next to the solution or project they locate, while list_rules and explain_rule use the rules folder in the directory the server was started from; without either, the tools fall back to the bundled rules.