@brxce/mcp-server
v2.1.0
Published
Model Context Protocol (MCP) server for BRXCE workspace management
Maintainers
Readme
BRXCE MCP Server
Model Context Protocol (MCP) server for BRXCE workspace management.
2.0.0: the default catalog is v2 — 29 domain tools (list/get/save per domain plus
brxce_deleteandbrxce_delete_permanently), on stdio and on the remotePOST /mcp. Breaking changes, the 1.x → v2 tool map and rollback (--catalog legacy,?catalog=legacy, pin1.3.0): docs/mcp/migration.md · CHANGELOG.
Installation
Default: remote URL + browser login (INT-5329)
The server is reachable over HTTP at the backend origin. Add the URL to your client, run the client's login command, sign in to BRXCE in the browser and approve the requested access. No token is copied by hand.
| Environment | URL |
|---|---|
| Production | https://api.brxce.com/mcp |
| make backend-local | http://localhost:3000/mcp |
The desktop app shows the same URL and snippets under Settings → MCP (the URL is derived from the backend address the app is built for).
Client support table
Same table and terms as the app and ADR 2026-09-16 §2. "Supported" is only declared after the end-to-end run (register → login → read → write → revoke → reconnect, INT-5331) passes; until then the two CLIs are pending verification and Claude Desktop is unverified (only binary strings were probed; no setup path is documented here on purpose).
| Client | Probed version | Status | How to add |
|---|---|---|---|
| Claude Code | 2.1.272 | pending verification | claude mcp add --transport http brxce https://api.brxce.com/mcp then claude mcp login brxce |
| Codex CLI | 0.153.4 | pending verification | config.toml: [mcp_servers.brxce] / url = "https://api.brxce.com/mcp", then codex mcp login brxce |
| Claude Desktop | 2.110.0 | unverified | Use the URL above where the app adds a remote server; steps not verified |
| Others (Cursor, ChatGPT, OpenClaw, …) | — | not a target | — |
Syntax source: official Claude Code and Codex CLI docs (probed 2026-09-16).
# Claude Code
claude mcp add --transport http brxce https://api.brxce.com/mcp
claude mcp login brxce# Codex CLI — config.toml
[mcp_servers.brxce]
url = "https://api.brxce.com/mcp"codex mcp login brxceConnection states
The app's Connections panel and the API agree on the vocabulary:
| State | Meaning | Where it comes from |
|---|---|---|
| Connected | grant status: "active" | GET /api/v1/oauth/grants |
| Revoked | grant status: "revoked" (all its tokens invalid) | DELETE /api/v1/oauth/grants/:id, password change, logout-all |
| Needs re-auth | client gets 401 after a revoke → run the login command again | client behaviour after revoke |
| Denied | consent declined → no grant is created, nothing is listed | consent page decision |
| Failed | the grants list itself could not be loaded | app error state |
Compatibility mode: stdio + API key
Existing stdio setups keep working and nothing is revoked automatically. Generate an API key in the desktop app (Settings → MCP → Compatibility mode), then:
# Global installation
npm install -g @brxce/mcp-server
# Or use with npx (no installation needed). Pin the version so a stale npx cache
# or a moved `latest` tag cannot start a different catalog than the one you tested.
npx -y @brxce/[email protected]
npx -y @brxce/[email protected] --version # prints the installed versionCodex/Claude Code setup, catalog modes (v2 default; consolidated / compat /
legacy for rollback), migration and rollback: docs/mcp/migration.md.
1.x tool → v2 tool map: catalog-v2.md §6.
Claude Desktop Configuration
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"brxce-mcp": {
"command": "npx",
"args": ["-y", "@brxce/[email protected]"],
"env": {
"AUTH_TOKEN": "<API_KEY>"
}
}
}
}Migrating to the URL flow: re-add the client with the URL above, sign in,
then revoke the old key in the app. The /mcp endpoint accepts API keys
during the transition; the switch-off date is a separate decision (ADR §6).
Optional environment variables:
BACKEND_URL: Custom backend URL (default: Production)MCP_DEBUG: Enable debug logging (default: false)
Agent Skill (Best-Practice Playbook)
This package ships a portable agent skill (brxce-mcp) that documents the
v2 catalog (the 2.0.0 default — 29 tools: brxce_list_* / brxce_get_* to
read, brxce_save_* to create or change, brxce_delete for restorable and
brxce_delete_permanently for irreversible deletes), common workflows, and
every known footgun. Upgrading from 1.x: re-run install-skill --force so the
old tool names disappear from your agent's instructions. It works in both
Claude Code and OpenClaw via the AgentSkills
standard.
Install
# Auto-detect Claude Code and/or OpenClaw and install the skill there
npx @brxce/mcp-server install-skill
# Force a specific target
npx @brxce/mcp-server install-skill --target=claude
npx @brxce/mcp-server install-skill --target=openclaw
npx @brxce/mcp-server install-skill --target=both
# Overwrite an existing skill directory
npx @brxce/mcp-server install-skill --forceWhat it installs
~/.claude/skills/brxce-mcp/(Claude Code)~/.openclaw/workspace/skills/brxce-mcp/(OpenClaw)
Each location receives a copy of SKILL.md plus the references/
directory (workflows, tool selection, footguns, error recovery).
After installing
Restart your agent so the skills cache picks up the new files:
- Claude Code: end the session and start a new one
- OpenClaw:
openclaw gateway restart
The skill is version-locked with the MCP server it ships with. When you
upgrade @brxce/mcp-server, re-run install-skill to refresh the docs.
Development
1. Build the server
pnpm build2. Run with MCP Inspector (Recommended)
The easiest way to run the inspector with the config file:
# From project root
make mcp-inspectThis will:
- Build the MCP server
- Use
inspector-config.jsonwith AUTH_TOKEN included - Open the browser with Inspector UI
- Test resources:
user://me,workspace://{id}
First-time setup:
If you don't have inspector-config.json:
- Copy from example:
cp inspector-config.example.json inspector-config.json - Add your AUTH_TOKEN (see "Getting an Auth Token" below)
Alternative methods:
Option A: Direct command with config file
npx @modelcontextprotocol/inspector --config inspector-config.json --server brxce-mcpOption B: Using command line flags (not recommended)
npx @modelcontextprotocol/inspector \
-e BACKEND_URL=http://localhost:3003/api/v1 \
-e MCP_DEBUG=true \
-e AUTH_TOKEN=your_token_here \
node dist/index.jsConfiguration
Environment Variables
Note: URL 설정은
@repo/config패키지에서 중앙 관리됩니다. 자세한 환경 설정은 ENVIRONMENT.md 참조.
| 변수 | 설명 | 기본값 |
|------|------|--------|
| BRXCE_API_URL | API 엔드포인트 | (환경별 기본값) |
| AUTH_TOKEN | JWT 인증 토큰 | (필수) |
| REFRESH_TOKEN | JWT 리프레시 토큰 | (선택) |
| MCP_DEBUG | 디버그 로깅 | false |
| BRXCE_MCP_CATALOG | v2(기본, 도구 29개), 롤백용 consolidated(통합 8개)·compat(통합 8개 + legacy 별칭)·legacy(1.x 개별 도구) | v2 |
레거시 BACKEND_URL도 하위 호환성을 위해 지원됩니다.
Available Servers in Config
The inspector-config.json includes two pre-configured servers:
- brxce-mcp: For Dev-Local environment (port 3003)
- brxce-mcp-local: For Local environment (port 3000)
Resources
The MCP server exposes the following resources:
user://me
Returns current user information along with their accessible workspaces.
Example response:
{
"user": {
"id": "user-id",
"email": "[email protected]",
"username": "username",
"name": "User Name",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
},
"workspaces": [
{
"id": "b7d3f0c8-4b91-4c2e-9c19-6f02c123abcd",
"name": "My Workspace",
"slug": "my-workspace",
"description": "Workspace description",
"workspaceType": "TEAM",
"planType": "FREE",
"spaceId": 1,
"userRole": "OWNER"
}
]
}workspace://{id}
Returns detailed information for a specific workspace.
Example:
workspace://b7d3f0c8-4b91-4c2e-9c19-6f02c123abcd- Get details for workspace UUIDb7d3f0c8-4b91-4c2e-9c19-6f02c123abcdworkspace://3f2c1a44-1b2c-4d5e-8f90-abcdef123456- Get details for workspace UUID3f2c1a44-1b2c-4d5e-8f90-abcdef123456
Development
Project Structure
src/
├── config/
│ └── environment.ts # Environment configuration
├── resources/
│ ├── user-resource.ts # User + Workspaces resource
│ └── workspace-resource.ts # Individual workspace resource
├── services/
│ └── api-client.ts # HTTP client for backend API
└── index.ts # MCP server entry pointScripts
# Development mode (watch)
pnpm dev
# Build
pnpm build
# Type checking
pnpm check-types
# Linting
pnpm lintMCP gate (make test-mcp)
The blocking CI check for MCP changes (.github/workflows/mcp.yml) and the local
command are the same script, scripts/ci/mcp-gate.sh:
make test-mcp # build @repo/{db,config,shared} + this package, then:
# mcp-server tsc --noEmit + vitest run (whole package)
# backend tsc --noEmit + vitest run tests/mcp-transport tests/oauth tests/mcp-e2e
make test-mcp-fast # same without the dist build (MCP_GATE_SKIP_BUILD=1)apps/backend/tests/mcp-e2e/ drives the real @modelcontextprotocol/client
(both protocol revisions) and this package's built stdio binary with the real
SDK v1 client through discovery → PKCE → consent → code → tools → refresh →
revoke → reconnect plus the security failure cases, against an in-memory OAuth
store. pnpm --filter backend exec tsx tests/mcp-e2e/serve.ts --port 3011
boots the same harness stand-alone for claude mcp add --transport http … /
codex mcp add … --url … checks (INT-5331).
Getting an Auth Token (local development / Inspector only)
End users never do this: they use the URL + login flow above, or an API key from the desktop app in compatibility mode. For the Inspector against a local backend you can use a session token:
- Using backend API:
# Login
curl -X POST http://localhost:3003/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "your-password"
}'Copy the
accessTokenfrom the responseUpdate
inspector-config.json:
{
"mcpServers": {
"brxce-mcp": {
...
"env": {
...
"AUTH_TOKEN": "paste-your-token-here"
}
}
}
}Troubleshooting
"Failed to fetch user" error
- Check if backend is running:
curl http://localhost:3003/health - Verify
BACKEND_URLmatches your backend port - Ensure
AUTH_TOKENis valid and not expired
"Network error when calling" error
- Verify backend is accessible
- Check firewall/network settings
- Ensure the backend port in
BACKEND_URLis correct
Inspector not connecting
- Make sure you've built the server first (
pnpm build) - Check that
dist/index.jsexists - Verify the path in
inspector-config.jsonis correct
Tool contract (INT-5330)
Every tool of the rollback catalogs (legacy / consolidated / compat), on both
transports (stdio and the backend's POST /mcp), follows one contract applied
at registration (src/registry/contract.ts). The v2 catalog has its own
contract — per-tool outputSchema, annotations from the §1 table, structured
errors with hint — described in
catalog-v2.md §3 and src/tools/v2/README.md:
- Annotations.
title,readOnlyHint,destructiveHint,idempotentHintandopenWorldHintare always present. They are derived from the effect classification insrc/registry/effects.ts(the permission list), soreadOnlyHintistrueexactly forreadoperations and a registration that claims otherwise is refused. Rule: read → read-only/idempotent; create → additive, not idempotent; update/action → not destructive, idempotent; delete/purge → destructive, idempotent; mixed-effect tools take the strongest effect and are never idempotent;openWorldHintis alwaysfalse. A tool may state a hint explicitly (seesrc/registry/annotations.ts). - Structured output.
brxce_discover,brxce_describe,brxce_list_workspacesandbrxce_list_workspace_membersdeclare anoutputSchemaand returnstructuredContent; the JSON text incontentis unchanged for text-only clients. The effect tools (brxce_read…brxce_purge) declare no output schema on purpose: a single-item call returns the operation's own result. _meta. Every result carries_meta["brxce/server"](name,version,revision) and_meta["brxce/tool"](name,effects).- Errors. A failure is
isError: trueplusstructuredContent.error(code,message,retryable, optionalstatus,hint,requiredScopes,grantedScopes,workspaceIds,operation,details). Registry codes are kept as they are; backend refusals map toinsufficient_scope(hint names thebrxce:writescope),forbidden_workspace,UNAUTHENTICATED,FORBIDDEN,NOT_FOUND,CONFLICT,RATE_LIMITED,UPSTREAM_ERRORorINVALID_ARGUMENT. The original text is not rewritten. - Identity.
serverInfooninitializeisbrxce-mcp-server+ the package version (src/version.ts);BRXCE_REVISION(the backend's/healthkey) adds the build sha. Thebrxce://serverresource reportsname,version,revision,transport(stdio|remote),protocolVersionsandcatalog.
v2 catalog (default since 2.0.0)
29 tools, one per domain action, each with its own input schema and
outputSchema — no discover/describe round trip. Reads are brxce_list_* /
brxce_get_*; brxce_save_* creates or updates (no id = create); deletes are
brxce_delete (trash, project, share — restorable) and
brxce_delete_permanently (everything else). Every write takes a required
requestId, sent as the REST Idempotency-Key: replaying the same call with
the same requestId returns the stored result (replayed: true) and changes
nothing. Worknodes accept a uuid or "#<number>". Contract:
catalog-v2.md; implementation notes:
src/tools/v2/README.md.
{"name":"brxce_save_worknodes","arguments":{"requestId":"req-2026-09-24-001",
"items":[{"workspaceId":"<ws>","title":"Copy review","nodeType":"todo"}]}}Rollback: BRXCE_MCP_CATALOG=legacy or brxce-mcp --catalog legacy (remote:
/mcp?catalog=legacy) restores the 1.x individual tools unchanged;
consolidated and compat are also still selectable. They are kept for one
release after 2.0.0.
Consolidated catalog (rollback; 1.x default)
The consolidated catalog was the 1.x default: eight tools — discover, describe, read,
create, update, action, delete and purge (all prefixed brxce_). Discover returns
compact, byte-bounded pages of operation IDs; describe returns the exact original
schema on demand. Every registered operation stays reachable without
operation-profile restarts, and the initial tool listing drops from ~139 KB to ~11 KB.
Selected with BRXCE_MCP_CATALOG=consolidated or --catalog consolidated; the CLI
flag wins over the environment variable.
Transition: BRXCE_MCP_CATALOG=compat keeps the eight tools and also lists every
legacy tool name as an alias with its original schema, so old prompts keep working in
the same session. An alias refuses input that needs a stronger tool (batch delete →
brxce_delete, batch update-status/update-priority → brxce_update). Retired
legacy names answer FEATURE_REMOVED. See migration.md.
Pass existing arguments under input, for example:
{"name":"brxce_read","arguments":{"operation":"brxce_list_workspaces","input":{}}}The candidate rejects unknown fields within input and effect mismatches. Mixed
batch deletion must use brxce_delete, and batch update-status / update-priority
must use brxce_update — the same tool the single-node update requires. Existing
handlers, previews, permissions and resources remain in use. Node >=18.17 is required. The eight-tool design is a candidate:
smaller initial discovery payload is measured, but full-task token/latency
superiority is not yet established.
See candidate validation and limitations.
One and many items in one envelope
Every executor takes either input (one item, identical to the legacy arguments) or
items[] (many) — for every operation, including scalar ones such as a workspace
update or a settings change, which are iterated with one outcome per item. mode is
required with items and is never defaulted.
{"name":"brxce_action","arguments":{
"operation":"brxce_batch_actions","mode":"commit","atomicity":"best_effort",
"items":[{"clientRef":"a","input":{"action":"archive","nodeIds":["n1"]}}]}}Every item is validated against the operation schema and re-checked against the
tool's effect before the first item runs, so a rejected item cannot surface after
part of the batch has already changed data. Per-item results carry clientRef,
status and, on failure, a message; include:"full" returns whole payloads.
Durable operations use the same entrypoints
Operations that run on the durable execution ledger have dotted ids
(worknode.status.update, …) and are reached through the same effect-specific
tools — never a universal execute tool. The effect is fixed by the tool, sent to the
backend, and the backend refuses an envelope whose operation has a different effect.
{"name":"brxce_update","arguments":{
"operation":"worknode.status.update","schemaVersion":"2.0.0","mode":"commit",
"requestId":"job-2026-09-16-1","items":[{"clientRef":"a","input":{"nodeId":"…","status":"done"}}]}}schemaVersion and mode are required, requestId is required to commit (it is the
idempotency key), and planToken carries a preview's plan. Anything the backend's own
capability list rules out — wrong effect, wrong schema version, atomic
without atomicChangeAndReceipt, preview without preview support, a plan-required
operation without a token — is refused before the request is sent.
maxItemsPerRequest bounds one backend request, not one job: a larger envelope is
split into requests of that size under a single jobId, so 100 items still go through
an operation whose request cap is 10. brxce_describe states the two limits separately
(maxItemsPerEnvelope, the server's BRXCE_MCP_MAX_ITEMS budget, and
maxItemsPerRequest, with autoChunked: true). Two cases are refused instead of split,
because splitting them would quietly break a promise: atomicity:"atomic" with more
than one item (no request boundary can span them), and a commit of a plan-required
operation with more items than one request holds (a planToken is bound to one request,
so the later ones would run with the plan dropped). A preview larger than one request
is previewed request by request and merged — every item result is kept and each chunk's
plan is published in plans[] with the clientRefs it covers, under a chunked note. Durable
operations are discovered lazily through brxce_discover (durableCatalog reports
whether the catalog was reachable) and described through brxce_describe, so the
initial tools/list stays at eight tools. When a legacy mutation has a durable
equivalent, its response names it in guarantees.durableAlternative — but only after
the backend's capability list confirms that operation exists with that effect.
Ids keep the backend allowlist's own spelling — dotted with camelCase
(meeting.agendaItem.create, notification.markRead) or underscores
(workspace.transfer_ownership, worknode.permanent_delete). The registry only
recognises the dotted shape; the allowlist stays the authority on which ids exist and
what effect each one has. When a capability publishes a per-item JSON Schema,
brxce_describe resolves the generator's $ref wrapper and returns the item object
itself (inputSchemaAvailable: true) so arguments can be built from describe alone;
where the backend publishes none, describe says inputSchemaAvailable: false rather
than inventing one. A commit answers with receiptId, results and counts at the
top level, and adds chunks only when the job really was split across several durable
requests.
The legacy path refuses requestId and planToken outright: it has no ledger, and
accepting an idempotency key it cannot honour would be a false guarantee.
What the legacy path does not provide: the response reports it in guarantees.
Items are iterated from this MCP process (execution:"client_iteration"), there is
no durable request ledger, requestId is echoed but idempotency is not enforced,
and atomicity:"atomic" is refused with ATOMICITY_UNSUPPORTED unless the operation
declares real atomic support and the envelope carries exactly one item — an
operation that is atomic per item is still executed item by item, so several items are
never grouped into one transaction and the request is refused before any write rather
than quietly downgraded. mode:"preview" is accepted only for operations whose
own contract implements a preview flag; otherwise it is refused rather than quietly
committing. Durable previews, receipts and idempotency are INT-5411.
Schema versions, errors and budgets
schemaVersion may be sent with any execution; if it does not match the operation's
version nothing runs and SCHEMA_VERSION_MISMATCH comes back. Registry-level
failures return a structured envelope — handler results and their errors pass through
byte-identical to legacy, so existing clients keep working:
{"error":{"code":"INVALID_ARGUMENT","message":"…","retryable":false,
"operation":"brxce_list_workspaces","details":{"issues":[{"path":"input.x","message":"…"}]}}}Codes: UNKNOWN_OPERATION, SCHEMA_VERSION_MISMATCH, INVALID_ARGUMENT,
INVALID_CURSOR, TOOL_EFFECT_MISMATCH, ATOMICITY_UNSUPPORTED,
FEATURE_UNAVAILABLE, FEATURE_REMOVED (a legacy name an older release registered; details.replacement when one exists), RESPONSE_TOO_LARGE. Permission, plan and receipt codes from
the execution contract are reserved and deliberately not emitted yet.
| Variable | Default | Effect |
|---|---|---|
| BRXCE_MCP_DISCOVER_BYTES | 8192 | Byte budget per discovery page. Whole operations move to the next page; entries are never cut in half. |
| BRXCE_MCP_MAX_RESPONSE_BYTES | 0 (off) | When set, an oversized read fails with RESPONSE_TOO_LARGE (nothing ran; narrow it and call again). An oversized mutation is never turned into an error — the change already happened, so it comes back as status:"applied" with payloadOmitted:true and instructions to re-query rather than resend. Setting it back to 0 is the rollback. |
| BRXCE_MCP_MAX_ITEMS | 1000 | Upper bound of items in one request. It is the only item cap: no operation is refused for being single-target — scalar operations are iterated with a per-item outcome. |
MCP cancellation is propagated to in-flight HTTP and attachment transfers only in the consolidated catalog, where the registry wraps every dispatch in the request signal context. The legacy (rollback) catalog registers handlers directly with the SDK, so its calls are not cancelled mid-flight. In neither mode does cancellation roll back a change the backend already applied.
brxce_discover cursors are opaque tokens bound to the catalog version and to the
query/domain/effect filters, as required by the execution contract; reusing one under
different filters returns INVALID_CURSOR instead of silently restarting. Discovery
also reports domain, schemaVersion and availability, and states that permission
is unknown until the backend evaluates the target resource — discovery is never an
authorization decision. brxce_describe takes one operation or up to three
operations and always returns complete schemas; it is intentionally exempt from the
page budget because a truncated schema would look executable.
