MCP Server is a local AI-agent integration server that exposes project context — TODO items, repository files, GitHub issues, session logs, and semantic search — to MCP-compatible clients (Claude Desktop, VS Code Copilot, Cursor) via both HTTP REST and MCP Streamable HTTP transports, and optionally over STDIO.
As a Windows service:
gsudo pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\build.ps1 UpdateServiceFrom the command line (development):
./build.ps1 StartServer --instance default
# or: dotnet run --project src/McpServer.Support.Mcp -- --instance defaultOver STDIO (for MCP clients that prefer stdin/stdout):
dotnet run --project src/McpServer.Support.Mcp -- --transport stdioThe default port is 7147. Configure it with:
Mcp:Portinappsettings.jsonPORTenvironment variable--urls http://+:PORTcommand-line argument
Workspace instances are hosted as in-process Kestrel listeners starting at port 7147.
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"mcp-server": {
"url": "<your-mcpserver-base-url>/mcp-transport"
}
}
}VS Code / Cursor (.vscode/mcp.json):
{
"servers": {
"mcp-server": {
"type": "sse",
"url": "<your-mcpserver-base-url>/mcp-transport"
}
}
}The MCP endpoint requires the header Accept: application/json, text/event-stream.
Two backends are available, configured via Mcp:TodoStorage:Provider:
| Provider | Storage | Best For |
|---|---|---|
yaml (default) |
docs/Project/TODO.yaml file |
Human-readable, version-controlled |
sqlite |
mcp.db SQLite database |
High-volume, concurrent access |
Persisted TODO IDs follow one of two canonical forms:
- Uppercase kebab-case ending in
-###for standard workspace TODOs (for example,PHASE0-REMOTE-001orMCP-TODO-CREATE-001) ISSUE-{number}for GitHub-backed TODOs (for example,ISSUE-17)
Create requests may also use ISSUE-NEW. The server immediately creates a GitHub issue, determines the
issue number, and saves the TODO using the canonical ISSUE-{number} id.
Yes. Bidirectional sync is available:
- GitHub → TODO:
POST /mcpserver/gh/issues/sync/from-github - TODO → GitHub:
POST /mcpserver/gh/issues/sync/to-github - Single issue:
POST /mcpserver/gh/issues/{number}/sync
Synced items get ISSUE-{number} IDs. Status changes (done ↔ closed) propagate in both directions.
For existing ISSUE-* items, MCP TODO priority is authoritative and syncs to canonical GitHub labels such
as priority: HIGH. After the first sync, ISSUE descriptions remain unchanged and later TODO updates add a
GitHub issue comment summarizing the change set.
A workspace maps a local folder (e.g., E:\github\MyProject) to a managed MCP instance with
its own port, TODO storage, and optional tunnel. Workspace configuration is stored in
Mcp:Workspaces within appsettings.json — not in the database — and is managed entirely
via the REST API.
curl -X POST http://localhost:7147/mcpserver/workspace \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_KEY" \
-d '{"workspacePath": "E:\\github\\MyProject"}'Defaults are applied automatically: name from last path segment, port auto-assigned from 7147+, TodoPath defaults to docs/todo.yaml.
POST /mcpserver/workspace/{key}/init scaffolds the workspace directory with:
- Creates directories as needed
- Creates an empty
todo.yamlat the configured TodoPath - Prepares the workspace for a child MCP instance
The {key} URL parameter is the Base64URL-encoded WorkspacePath. For example, E:\github\MyProject encodes to RTogaXRodWJcTXlQcm9qZWN0.
GET /mcpserver/tools/search?keyword=screenshot searches across:
- Tags — bidirectional contains match (handles singular/plural, e.g.,
screenshotmatchesscreenshots) - Tool name — case-insensitive contains
- Tool description — case-insensitive contains
Results include both global tools (no workspace scope) and workspace-specific tools.
Buckets are GitHub repositories that serve as package registries for tool definitions, similar to Scoop buckets. They contain JSON manifest files describing tools. You can:
- Add a bucket:
POST /mcpserver/tools/bucketswith{owner, repo, branch, path} - Browse tools:
GET /mcpserver/tools/buckets/{name}/browse - Install a tool:
POST /mcpserver/tools/buckets/{name}/install?tool=mytool - Sync all:
POST /mcpserver/tools/buckets/{name}/sync
Buckets use the gh CLI to read repository contents.
Per-workspace auth tokens are generated on each service restart and written into the AGENTS-README-FIRST.yaml marker file in each workspace root. All /mcpserver/* endpoints require the token via:
- Header:
X-Api-Key: YOUR_TOKEN - Query parameter:
?api_key=YOUR_TOKEN
Agents read the token from the marker file and include it in requests. Tokens are not persisted — they rotate automatically on restart.
Navigate to http://localhost:7147/pair in a browser. This provides a login form for authorized users (configured in Mcp:PairingUsers). After authentication, the API key is displayed for copy-paste into MCP client configs.
Passwords are stored as SHA-256 hashes and verified with constant-time comparison to prevent timing attacks. Session cookies are HttpOnly and Secure (when HTTPS).
| Controller | Public Endpoints |
|---|---|
| Workspace | GET /mcpserver/workspace, GET /mcpserver/workspace/{key}, GET /mcpserver/workspace/{key}/status |
| Tool Registry | GET /mcpserver/tools/search, GET /mcpserver/tools, GET /mcpserver/tools/{id} |
| Health | GET /health, GET /alive |
| Provider | Config Key | Use Case |
|---|---|---|
| ngrok | ngrok |
SaaS, instant public URLs, zero infrastructure |
| Cloudflare Tunnel | cloudflare |
Free tier, integrates with Cloudflare DNS |
| FRP | frp |
Fully self-hosted, open-source (Apache-2.0) |
Configure via Mcp:Tunnel:Provider in appsettings.json.
Detailed runbooks:
docs/Operations/Tunnel-Ngrok.mddocs/Operations/Tunnel-Cloudflare.mddocs/Operations/Tunnel-FRP-Railway.md
- Deploy
frpson a VPS:docker compose -f docker-compose.frps.yml up -d - Set
Mcp:Tunnel:Providertofrp - Configure
Mcp:Tunnel:Frpwith your server address, port, and token - The MCP server starts
frpcautomatically on launch
Yes. When Mcp:Tunnel:Provider is set to a non-empty value, the tunnel starts as a hosted service with the application lifecycle and stops on shutdown.
Yes. The auth token is passed via the NGROK_AUTHTOKEN environment variable, not as a CLI argument, preventing exposure in process listings.
The context search endpoint (POST /mcpserver/context/search) combines:
- FTS5 full-text search — BM25-ranked SQLite FTS5 with snippet extraction
- HNSW vector search — cosine-similarity nearest-neighbor using all-MiniLM-L6-v2 embeddings (384 dimensions)
- Reciprocal Rank Fusion — merges both result sets with configurable weights
If embeddings are unavailable, search degrades gracefully to FTS5-only.
The ingestion pipeline indexes:
- Repository files (under configured allowlist)
- Session logs (Markdown and JSON formats)
- GitHub issues and PRs (via
ghCLI) - External docs (from cached
docs/external/path)
Trigger a full re-index with POST /mcpserver/sync/run.
POST /mcpserver/context/pack produces a deterministic collection of ranked context chunks — a curated bundle of relevant content for an AI agent's prompt context.
| Feature | REST API (/mcpserver/*) |
MCP Transport (/mcp-transport) |
|---|---|---|
| Protocol | Standard HTTP/JSON | MCP Streamable HTTP (JSON-RPC) |
| Clients | Any HTTP client, curl, Swagger | Claude Desktop, VS Code Copilot, Cursor |
| Tools | N/A (endpoints) | todo_*, context_*, repo_*, gh_*, sync_*, sessionlog_* |
| Discovery | OpenAPI/Swagger | MCP tool listing |
Both share the same backend services and run on the same port.
| Group | Tools |
|---|---|
| TODO | todo_list, todo_get, todo_create, todo_update, todo_delete |
| Context | context_search, context_pack |
| Repository | repo_read, repo_write, repo_list |
| Sync | sync_run, sync_status |
| GitHub | gh_list_issues, gh_get_issue, gh_create_issue, gh_comment_issue, gh_list_pulls, gh_comment_pull |
| Session Log | sessionlog_submit, sessionlog_query |
gsudo pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\build.ps1 UpdateServiceThe Nuke target handles elevation, backup/restore, publish, service registration/update, restart, and health verification. The service is configured with auto-start and automatic recovery on failure.
Published to C:\ProgramData\McpServer\ as a self-contained single-file executable. Configuration is at C:\ProgramData\McpServer\appsettings.json.
gsudo pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\build.ps1 UpdateServiceThe Nuke target stops the service, creates backups, publishes, restores configuration and data, restarts the service, and verifies health. A timestamped archive is saved to %USERPROFILE%\McpServer-Backups\ for rollback. Do not update the Windows service by manually copying files or by running lower-level deployment scripts directly.
| Action | Description |
|---|---|
Install |
Publish + create Windows service |
Uninstall |
Stop + remove service |
Start |
Start the service |
Stop |
Stop the service |
Restart |
Stop + Start |
Status |
Show service info + health check |
Publish |
Build and publish without service changes |
Another process is listening on port 7147. Find and stop it:
Get-NetTCPConnection -LocalPort 7147 | Select-Object OwningProcess
Stop-Process -Id <PID>Or change the port in appsettings.json under Mcp:Port.
The /mcp-transport endpoint requires the header:
Accept: application/json, text/event-stream
Ensure your MCP client sends this header. Standard REST clients should use the /mcpserver/* endpoints instead.
Check that the database is accessible and the configured DataDirectory exists. Review logs at the configured Serilog file sink path (default: logs/mcp-.log).
Ensure tools have been registered with appropriate tags. Search matches tags bidirectionally — screenshot matches tools tagged screenshots and vice versa. Check both global tools and workspace-scoped tools.
Ensure gh is installed and authenticated:
gh auth status
gh auth loginThe server uses the local gh CLI authentication — no API tokens are configured separately.
The server uses a three-tier resolution chain to determine which workspace a request targets:
X-Workspace-Pathheader (highest priority) — Send the absolute workspace path in this header. Returns 400 if the path is not a registered workspace.- API key reverse lookup — The
X-Api-Keytoken is mapped back to its generating workspace viaWorkspaceTokenService. - Default workspace (lowest priority) — Falls back to the primary workspace from configuration.
No. All workspaces are served on a single shared port. Use the X-Workspace-Path header to target a specific workspace. Each workspace still gets its own API key in its marker file, which can also be used for workspace resolution.
The Director TUI reuses the same base URL for all workspaces and changes only the X-Workspace-Path header when switching workspaces. No need to re-read marker files for workspace switching after the initial connection.
All workspace data is stored in a single shared SQLite database. Each entity table has a WorkspaceId column, and EF Core global query filters automatically scope all queries to the active workspace. Admin operations can use IgnoreQueryFilters() for cross-workspace queries.