Skip to main content
Glama

local-code-mcp

local-code-mcp is a reusable, profile-driven MCP server for safely inspecting and changing local Git repositories through a Streamable HTTP endpoint. RentingHub is the first profile/plugin; no RentingHub business logic is embedded in the core server.

The security model is deny-by-default:

  • binds to 127.0.0.1 by default;

  • file, Git, push, force-push, and destructive modes are disabled by default;

  • no raw shell tool exists;

  • commands are immutable argv arrays loaded from the active profile and run with shell: false;

  • every file path is confined to the configured repository, including symlink resolution;

  • secret-like and profile-denied paths are blocked;

  • direct pushes to main, master, or the profile default branch are blocked;

  • all tool calls are audited to logs/audit.log with sensitive arguments redacted.

Requirements

  • Node.js 20 or newer

  • Git

  • rg is optional; search_code falls back to a Node implementation

  • GitHub CLI gh is optional and required only for automatic PR creation

  • Tailscale is optional and remains outside the Node dependency graph

The included RentingHub profile currently points to:

/Volumes/Home_EX/Projects/RentingHub

Update profiles/rentinghub.json or set MCP_PROFILE_PATH if the repository is elsewhere.

Related MCP server: github-mcp

Install and run

export MCP_HOST=127.0.0.1
export MCP_PORT=3333
export MCP_ACTIVE_PROFILE=rentinghub
export MCP_PROFILE_PATH="$PWD/profiles/rentinghub.json"
export MCP_AUTH_MODE=bearer
export MCP_BEARER_TOKEN="$(openssl rand -hex 32)"
export MCP_PUBLIC_MODE=true
export MCP_ENABLE_FILE_WRITE=false
export MCP_ENABLE_GIT_WRITE=false
export MCP_ENABLE_PUSH=false
export MCP_ENABLE_DESTRUCTIVE=false
export MCP_TOOL_CATALOG_MODE=chatgpt_default

npm install
npm run build
npm run start

Health check:

curl http://127.0.0.1:3333/health

The MCP endpoint is:

http://127.0.0.1:3333/mcp

Supported environment variables:

MCP_HOST=127.0.0.1
MCP_PORT=3333
MCP_ACTIVE_PROFILE=rentinghub
MCP_PROFILE_PATH=/absolute/path/to/profiles/rentinghub.json
MCP_AUTH_MODE=none|bearer|oauth
MCP_BEARER_TOKEN=<required when exposed remotely>
MCP_PUBLIC_BASE_URL=https://host.example
MCP_OAUTH_ISSUER=https://host.example
MCP_OAUTH_ADMIN_SECRET=<local approval secret>
MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS=3600
MCP_OAUTH_REFRESH_TOKEN_TTL_SECONDS=2592000
MCP_OAUTH_STORAGE_PATH=./data/oauth-store.json
MCP_OAUTH_DEBUG=false
MCP_ALLOWED_ORIGINS=<comma-separated exact origins>
MCP_PUBLIC_MODE=false
MCP_ENABLE_FILE_WRITE=false
MCP_ENABLE_GIT_WRITE=false
MCP_ENABLE_PUSH=false
MCP_ENABLE_DESTRUCTIVE=false
MCP_ENABLE_FORCE_PUSH=false
MCP_ENABLE_GH_WRITE=false
MCP_ENABLE_GH_MERGE=false
MCP_ENABLE_GIT_REWRITE=false
MCP_GIT_REMOTE=origin
MCP_MAX_READ_BYTES=262144
MCP_MAX_OUTPUT_BYTES=200000
MCP_TOOL_CATALOG_MODE=chatgpt_default|power_user|debug (default: chatgpt_default)

MCP_TOOL_CATALOG_MODE restricts which tools are exposed to the AI client to prevent ChatGPT/Cursor from picking low-level or deprecated tools (like apply_patch or write_file) in favor of preferred high-level ones (like apply_patch_batch and run_targeted_validation).

  • chatgpt_default: Hides legacy and low-level tools, registering only preferred high-level tools.

  • power_user: Exposes all tools in the project profile except explicitly deprecated ones.

  • debug: Exposes all tools configured in the project profile.

MCP_AUTH_MODE defaults to bearer, preserving the existing Codex CLI flow. In bearer mode, every /mcp request must include the configured token. In OAuth mode, every /mcp request must include a server-issued OAuth access token. none is intended only for local development; public no-auth mode prints a prominent warning. If an Origin header is present in bearer/none mode, it must exactly match MCP_ALLOWED_ORIGINS.

Tailscale Funnel

Start the local server first, then expose its port:

tailscale funnel 3333

For background mode:

tailscale funnel --bg 3333

The public endpoint will be:

https://<machine>.<tailnet>.ts.net/mcp

Verify and disable Funnel:

tailscale funnel status
tailscale funnel reset
tailscale funnel status

Do not keep Funnel enabled when it is not in use. Never expose this server publicly without bearer or OAuth authentication. Tailscale Funnel publishes to the internet; it is not limited to devices in your tailnet.

Codex configuration

Export the same bearer token in the environment that starts Codex:

export LOCAL_CODE_MCP_TOKEN="<same token used by MCP_BEARER_TOKEN>"

Add this to ~/.codex/config.toml or a trusted project's .codex/config.toml:

[mcp_servers.local_code_mcp]
url = "https://<machine>.<tailnet>.ts.net/mcp"
bearer_token_env_var = "LOCAL_CODE_MCP_TOKEN"
enabled = true
startup_timeout_sec = 20
tool_timeout_sec = 120
default_tools_approval_mode = "prompt"

enabled_tools = [
  "repo_info",
  "list_files",
  "search_code",
  "read_file",
  "git_status",
  "git_diff",
  "inspect_package_scripts",
  "read_test_failures",
  "apply_patch",
  "run_check",
  "run_validation_sequence",
  "git_create_branch",
  "git_add",
  "git_commit",
  "git_push_branch",
  "github_create_pr",
  "rentinghub_scan_architecture",
  "rentinghub_run_core_validation"
]

[mcp_servers.local_code_mcp.tools.apply_patch]
approval_mode = "approve"

[mcp_servers.local_code_mcp.tools.run_check]
approval_mode = "approve"

[mcp_servers.local_code_mcp.tools.run_validation_sequence]
approval_mode = "approve"

[mcp_servers.local_code_mcp.tools.git_add]
approval_mode = "approve"

[mcp_servers.local_code_mcp.tools.git_commit]
approval_mode = "approve"

[mcp_servers.local_code_mcp.tools.git_push_branch]
approval_mode = "approve"

[mcp_servers.local_code_mcp.tools.github_create_pr]
approval_mode = "approve"

Codex supports Streamable HTTP URLs, bearer tokens sourced from environment variables, tool allowlists, and per-tool approval modes. Restart Codex after changing its configuration.

ChatGPT Web OAuth setup

Start the server in OAuth mode with every write gate disabled:

cd /Volumes/Home_EX/Projects/local-code-mcp

export MCP_HOST=127.0.0.1
export MCP_PORT=3333
export MCP_ACTIVE_PROFILE=rentinghub
export MCP_PROFILE_PATH="$PWD/profiles/rentinghub.json"

export MCP_AUTH_MODE=oauth
export MCP_PUBLIC_MODE=true
export MCP_PUBLIC_BASE_URL="https://ats-mac-mini.tail60fb71.ts.net"
export MCP_OAUTH_ISSUER="https://ats-mac-mini.tail60fb71.ts.net"
export MCP_OAUTH_ADMIN_SECRET="$(openssl rand -hex 16)"

export MCP_ENABLE_FILE_WRITE=false
export MCP_ENABLE_GIT_WRITE=false
export MCP_ENABLE_PUSH=false
export MCP_ENABLE_DESTRUCTIVE=false

echo "ADMIN SECRET FOR OAUTH APPROVAL:"
echo "$MCP_OAUTH_ADMIN_SECRET"

npm run build
npm start

In another terminal, expose the local port:

tailscale funnel --bg 3333
tailscale funnel status

ChatGPT configuration / Cấu hình ChatGPT:

  1. Open Settings / Cài đặt.

  2. Open Apps & Connectors / Ứng dụng & trình kết nối.

  3. Open Advanced settings / Cài đặt nâng cao.

  4. Enable Developer mode / Chế độ nhà phát triển.

  5. Select Create / Tạo.

  6. Server URL / URL máy chủ: https://ats-mac-mini.tail60fb71.ts.net/mcp.

  7. Authentication / Xác thực: OAuth.

  8. Select Create / Scan Tools.

  9. On the local-code-mcp approval page, enter MCP_OAUTH_ADMIN_SECRET; approval is never automatic.

The OAuth server publishes protected-resource metadata, authorization-server/OpenID metadata, dynamic client registration, authorization with PKCE S256, token and refresh-token endpoints, and an empty JWKS because access tokens are opaque and strictly stored/verified server-side.

Set MCP_OAUTH_DEBUG=true temporarily to log OAuth request method, path, status, MCP authentication failures, and safely redacted rejected redirect locations. Debug logging never includes tokens, authorization codes, PKCE verifiers, or the admin secret.

Run the local OAuth smoke test after building:

npm run smoke:oauth

OAuth scopes authorize a tool, but they do not enable it. The environment gates remain authoritative. To enable full write access manually for a limited session:

export MCP_ENABLE_FILE_WRITE=true
export MCP_ENABLE_GIT_WRITE=true
export MCP_ENABLE_PUSH=true
export MCP_ENABLE_DESTRUCTIVE=true

Restart the server after changing gates. Never use MCP_AUTH_MODE=none with write enabled. Do not keep Funnel enabled when MCP is not in use. Do not expose logs/ or data/oauth-store.json. Do not auto-merge pull requests.

Codex-style Project Trust

The server implements a Codex-style project trust system to manage multiple projects safely, switch projects at runtime, and enforce permissions.

Trust store path: ~/.local/share/local-code-mcp/config.toml (or configured via MCP_TRUST_STORE_PATH).

Commands

mcptrust trust rentinghub \
  --repo /Volumes/Home_EX/Projects/RentingHub \
  --profile-path /Volumes/Home_EX/Projects/local-code-mcp/profiles/rentinghub.json \
  --github Phamtuandat/RentingHub \
  --mode full \
  --default \
  --yes

mcpfull rentinghub
mcpread /Volumes/Home_EX/Projects/UnknownRepo
mcptrust list
mcptrust current
mcptrust doctor
mcpswitch mentorly
mcpstop

Interactive Codex-style Prompt

When running mcpfull or mcpread on an untrusted directory, the CLI will ask for explicit approval:

mcpfull /Volumes/Home_EX/Projects/Mentorly

Project is not trusted:
/Volumes/Home_EX/Projects/Mentorly

Trust this folder with full workspace permissions? [y/N]
  • If y/yes is selected, the project is verified (must contain .git), registered in the trust store, and started.

  • If n/no is selected for mcpfull, the startup is refused.

  • If n/no is selected for mcpread, it starts in an untrusted read-only mode (skipping local config files).

Safety & Isolation Constraints

  • No Sandbox Bypasses: Trusting a project does not bypass the repository sandbox.

  • Canonical realpath behavior: All project directories are normalized and stored using fs.realpath resolution. Symlinks and real paths match interchangeably.

  • Untrusted read-only: Untrusted projects are strictly read-only and will not load any project-local .mcp/project.toml configurations.

  • Project-Local Config: Local configurations are only loaded for trusted projects, allowing command overrides and validation sequence customization.

  • Safe Command Policy (SafeCommandPolicy): Project-local commands defined in .mcp/project.toml are strictly validated:

    • Local config can only override existing global profile commands, or add new commands passing the safe policy.

    • Allowed safe binaries: npm, npx, node, pnpm, yarn, bun, dotnet, cargo, go, python, python3, pytest, vitest, jest, tsc, eslint, prettier.

    • Forbidden/dangerous binaries are strictly blocked: rm, sudo, su, chmod, chown, curl, wget, ssh, scp, rsync, dd, mkfs, diskutil, osascript, open, launchctl, kill, pkill, docker, docker-compose, bash, sh, zsh, fish, powershell, pwsh.

    • Shell metacharacters are strictly forbidden: ;, &&, ||, |, >, >>, <, `, $(, ${, \n, \r.

    • The cwd of any command must reside within the repository sandbox.

    • Commands cannot read denied paths or contain absolute paths pointing outside the repository.

  • Immutable Safety Defaults: Local configs cannot enable force-push (enableForcePush), GitHub merge (enableGhMerge), or destructive actions. Any attempt will log warnings.

  • No-Restart Runtime Switching: Switch between projects using mcpswitch <project> or the switch_project MCP tool without restarting the HTTP/OAuth server process. Note:

    • Runtime switching does not require server restart for repo context, mode, or commands.

    • However, MCP tools are registered during initial session setup. If the project-local config changes the list of enabledTools, the MCP client might need to reconnect/reinitialize the session to reflect changes in the visible tool catalog.

  • OAuth Connector Compatibility: If additional scopes are introduced in the MCP server, the ChatGPT connector must be reconnected to request the updated scope list.

  • When to Restart: Restart is only needed when modifying server environment variables, OAuth credentials, or core local-code-mcp code.

Full Git/GH toolset

Daily Workflow

  1. repo_info

  2. git_status

  3. git_create_and_checkout_branch

  4. apply_patch

  5. run_validation_sequence

  6. git_diff

  7. git_add

  8. git_commit

  9. git_push_branch

  10. gh_pr_create

  11. gh_pr_checks

  12. gh_pr_merge (only if explicitly enabled)

Environment Gates

Configure the following environment variables to control permissions:

MCP_ENABLE_FILE_WRITE=true
MCP_ENABLE_GIT_WRITE=true
MCP_ENABLE_PUSH=true
MCP_ENABLE_DESTRUCTIVE=true
MCP_ENABLE_GH_WRITE=true
MCP_ENABLE_GH_MERGE=false
MCP_ENABLE_GIT_REWRITE=false
MCP_ENABLE_FORCE_PUSH=false

Profiles and plugins

Profiles are JSON files that define:

  • repository root and GitHub repository;

  • default branch;

  • denied path patterns;

  • immutable allowlisted commands;

  • validation sequence;

  • enabled tools;

  • optional project plugin.

The core loads a plugin by projectPlugin. Plugins receive the same constrained tool context and cannot replace the path sandbox or command allowlist. The RentingHub plugin adds architecture discovery and the core validation sequence.

To support another project, create a new profile and optionally register a narrowly scoped plugin in src/projectProfiles/plugins.ts.

Write workflow

Recommended progression:

  1. Read-only inspection.

  2. MCP_ENABLE_FILE_WRITE=true for apply_patch, new files, and moves.

  3. MCP_ENABLE_GIT_WRITE=true for branch, stage, and commit operations.

  4. MCP_ENABLE_PUSH=true only for pushing a non-default branch and creating a PR.

write_file refuses to replace an existing file unless destructive mode is also enabled. delete_file requires destructive mode. Force push is disabled unless MCP_ENABLE_FORCE_PUSH=true, and then uses --force-with-lease; protected/default branch pushes remain forbidden.

Running allowlisted checks

run_check never executes arbitrary shell text. It resolves an exact command key or a safe alias to an existing allowedCommands entry in the active profile, then executes that entry's immutable argv array with shell: false.

Examples:

run_check({ "commandKey": "backend_build" })
run_check({ "commandKey": "backend_jest_run_in_band" })
run_validation_sequence({ "stopOnFailure": true })

Aliases such as npm run build, build, and npm test are accepted only when they uniquely map to a configured profile command. Unknown commands are rejected and the response lists available command keys and aliases.

Testing

npm test
npm run build
npm run smoke
npm run smoke:oauth

The smoke test starts the built server on a test port, checks /health, verifies unauthenticated MCP rejection, and performs an authenticated MCP initialize request.

Performance-oriented MCP workflows

This server supports performance-optimized workflows that reduce tool round-trip latency by providing batch operations, targeted validation, background jobs, project indexing, and task context.

Why:

  • Fewer Round-trips: Consolidate multiple inspections and validation sequences into single tool calls.

  • Batch File Reads: Read multiple files at once.

  • Batch Patching: Apply multiple patches atomically.

  • Targeted Validation: Validate changes quickly or thoroughly using allowlisted validation suites.

  • Background Jobs: Execute longer-running validation or indexing tasks asynchronously without blocking the client.

  • Project Index: Instant access to directory layout and file structures.

Example Daily Workflow:

1. workflow_summary
2. task_context(query)
3. read_files_batch(paths)
4. apply_patch_batch(patches)
5. run_targeted_validation(level="quick")
6. read_test_failures_smart (if quick validation failed)
7. run_targeted_validation(level="full")
8. git_diff
9. git_commit

Background Job Workflow:

start_code_job
get_code_job_status
get_code_job_result
  • Deterministic Workflows Only: The background job queue does not make the MCP server an autonomous AI. It only runs pre-configured, deterministic local operations (indexing, validation, Git branch creation/switching, and optional auto-commits). It does not generate code.

  • Write Permission Gate: Applying patches or auto-committing via jobs still respects write environment gates and requires explicit user approval.

Security limitations

  • This is an application-level repository sandbox, not a VM or OS account boundary. Run it as a dedicated low-privilege user for stronger isolation.

  • Bearer and opaque OAuth access tokens are replayable if stolen. Rotate/revoke local OAuth storage after suspected exposure and avoid shell history, screenshots, logs, and committed config.

  • Tailscale Funnel makes the endpoint internet reachable. Rate limiting and network-level abuse controls are not implemented here.

  • Allowed project commands execute with the server process user's permissions and can run project lifecycle behavior. Keep profile command definitions minimal and reviewed.

  • Git hooks may execute during commit/push. Review repository hooks and use a dedicated environment for untrusted repositories.

  • gh uses the locally authenticated GitHub account. Scope that account/token to only the repositories required.

  • Audit logs and check logs can contain repository output. They are mode 0600, ignored by Git, and should be retained and deleted according to local policy.

  • The origin allowlist is supplementary; bearer/OAuth token verification is the primary control for MCP requests.

  • OAuth approval has no built-in rate limiter. Keep the admin secret strong, temporary, and private, and rely on Funnel/network controls to limit exposure.

F
license - not found
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Phamtuandat/local-code-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server