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_solutionalso acceptsinclude_pathsandexclude_paths, each a list of glob patterns such assrc/**/*.csortests/**. Wheninclude_pathsis 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 itsisCancelledflag set.analyze_difftakes the changes as exactly one of two parameters:diff(raw unified diff text, e.g. the output ofgit diff) orgit_ref(a ref range such asmain..HEAD, which the server resolves by runninggit diffitself). Passing both, or neither, is an error.analyze_diffwithinclude_context: trueadditionally 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 separatecontextFindingscollection, never overlap with the direct findings, and respectmin_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:
- Scaffold —
scaffold_ruleturns a one-line description into a.cgrskeleton with the frontmatter filled in. - Validate —
validate_rulechecks that the draft parses and that the frontmatter is complete. Syntax mistakes surface here with a hint pointing at the matchingget_authoring_docstopic. - Dry-run —
dry_run_ruleruns the draft against your real code without saving it, so you can see over- and under-matching before committing. - Spec —
test_rule_specruns the draft against a.spec.mdof explicit hits and misses. A passing spec means the rule does what you intended. - Save —
save_rulewrites the validated draft to the./rulesdirectory in your workspace, wherecodecharter analyzepicks 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.
Related
- codecharter validate and codecharter test
are the CLI equivalents of
validate_ruleandtest_rule_spec. - Writing rules covers the DSL the assistant drafts in.