Sign inSign up

sinups/layers-mcp-server

By sinups

•Updated about 3 hours ago

MCP server for Layers — connect Claude, Cursor, Windsurf to your workspace (73 tools)

Image
API management
0

10K+

sinups/layers-mcp-server repository overview

⁠Layers MCP Server

A Model Context Protocol⁠ server for Layers⁠. Connect assistants to your workspace to read and manage tasks, pages, projects and forms with your account's permissions.

Docker Hub

Protocol: MCP Streamable HTTP (2025-06-18 spec) | Tools: 73 | Transport: HTTP

⁠Quick Start

The endpoint is https://YOUR_DOMAIN/mcp. Use the domain of your own installation. The connection guide⁠ covers both OAuth and manually generated tokens. A self-hosted instance serves its own guide at /mcp/docs/start/connect/ and copyable settings at /mcp/setup.

⁠OAuth: ChatGPT, Claude and development clients
  • ChatGPT web: create a custom app under Settings > Apps when your account and administrator permit it. Enable developer mode if required, enter the endpoint, select OAuth, complete sign-in and tool discovery, then select the app in a chat. This is separate from publication in the public directory.
  • Claude web / Desktop: add a custom connector under Settings > Connectors and sign in through the browser. A remote connector is reached from Anthropic's cloud, not your laptop; a private-network server needs an appropriate connection path.
  • Claude Code:
claude mcp add --transport http layers https://YOUR_DOMAIN/mcp

Open /mcp inside Claude Code to authenticate. claude mcp list checks connection status. Tool discovery on the default full catalogue should show layers with 73 tools; the number of available tools depends on the selected catalogue.

  • Cursor: add a native HTTP server to .cursor/mcp.json:
{
  "mcpServers": {
    "layers": {
      "url": "https://YOUR_DOMAIN/mcp"
    }
  }
}
  • VS Code: add a native HTTP server to .vscode/mcp.json:
{
  "servers": {
    "layers": {
      "type": "http",
      "url": "https://YOUR_DOMAIN/mcp"
    }
  }
}

Start the connection and follow the client's authentication prompt. Other clients may have different configuration formats; remote HTTP support does not itself guarantee OAuth support.

⁠Personal token: scripts and clients supporting headers

Open Account Settings > Access Tokens, create a named token with an expiry, and copy it once. Start with read-only access for inspection; enable writes only when needed. The token cannot grant rights your account does not have.

Send Authorization: Bearer YOUR_TOKEN to the endpoint. For a client that accepts headers in this configuration:

{
  "mcpServers": {
    "layers": {
      "url": "https://YOUR_DOMAIN/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_TOKEN"
      }
    }
  }
}

Keep actual tokens out of Git, URLs, screenshots and messages. Use protected client settings or environment-backed secrets. Revoke a compromised token in account settings.

⁠Local bridge for stdio-only clients

For Claude Desktop's local configuration or another stdio-only client, install a supported Node.js LTS version compatible with mcp-remote and use:

{
  "mcpServers": {
    "layers": {
      "command": "npx",
      "args": ["-y", "mcp-remote@latest", "https://YOUR_DOMAIN/mcp"]
    }
  }
}

The bridge can perform OAuth. For token authentication, add "--header", "Authorization:Bearer YOUR_TOKEN" to args. Do not commit a real token. Claude Desktop reads local configuration from ~/Library/Application Support/Claude/claude_desktop_config.json on macOS or %APPDATA%\\Claude\\claude_desktop_config.json on Windows. Fully quit and reopen the app after editing; if npx is not found, use its full path.

⁠Check the connection safely

Ask which workspace and projects are accessible before testing writes. A successful GET of /mcp shows the server version, not proof that authenticated tool calls work. Use ?profile=pages, tasks, forms or another supported profile for a smaller catalogue. A readonly catalogue hides writes; it does not change the token's rights.

If a write times out, read the object before retrying: the change may already have happened. Review the troubleshooting guide⁠ and data handling⁠ before deployment.

⁠Self-Hosted Deployment

Add the MCP server to your Layers Docker Compose stack.

⁠Where it sits in the Layers self-host stack

If you are standing up the full self-hosted Layers bundle — the Layers server, this MCP server, and the Layers AI Chat⁠ — this service is the middle layer. The request flow is:

browser → AI Chat → (internal network) MCP server :8091 → Layers REST API → Postgres

The chat (and any other in-cluster consumer) talks to the MCP server over the internal Docker network, sending each user's workspace token as a bearer:

LAYERS_MCP_URL = http://layers-mcp:8091/mcp

Internal endpoint vs. public endpoint. http://layers-mcp:8091/mcp is the internal address, reachable only from inside the Docker network and authorized by the forwarded Authorization: Bearer layers_ws_…. If you also expose /mcp publicly through your reverse proxy (for external AI assistants), that public URL normally sits behind your own SSO / proxy authorization and is a separate entry point. In-stack consumers like the chat must target the internal layers-mcp:8091 address directly — not the public URL.

Issuing the workspace token the chat uses. The chat sends a Layers workspace token as the bearer. Create it once in your Layers instance under Settings > Access Tokens (it starts with layers_ws_… and is shown only once), then hand it to the chat as described in its hosting guide⁠.

In the self-host bundle the image is pulled as hissih/layers-mcp-server⁠; the env vars below (LAYERS_BACKEND_BASEURL, LAYERS_BACKEND_FORWARDINCOMINGAUTH, …) are the same regardless of which tag you pull. Point LAYERS_BACKEND_BASEURL at the Layers API over the internal network (e.g. http://layers-server:8080). The full end-to-end stack assembly (domains, SSO cookie, reverse proxy, voice) is documented in the chat repo — see its README⁠ and docs/hosting-guide.md⁠.

⁠Docker Compose

Add this service alongside your existing layers-server and layers-db:

layers-mcp:
  image: sinups/layers-mcp-server:latest
  restart: always
  depends_on:
    - layers-server
  environment:
    LAYERS_BACKEND_BASEURL: http://layers-server:8080
    LAYERS_BACKEND_FORWARDINCOMINGAUTH: "true"
    LAYERS_BACKEND_REQUIREINCOMINGAUTH: "true"
    LAYERS_BACKEND_BEARERTOKEN: ""
    # Address of the web app as a person opens it — links in tool output come from here
    LAYERS_BACKEND_FRONTENDBASEURL: https://app.example.com
    LAYERS_MCP_ENDPOINT: /mcp
    SERVER_PORT: "8091"
    SPRING_PROFILES_ACTIVE: server
  ports:
    - "8091:8091"
  networks:
    - layers-internal

Users connect to https://YOUR_DOMAIN/mcp using their personal API tokens from Settings > Access Tokens.

⁠Reverse Proxy

If your Layers instance uses a reverse proxy (nginx, Traefik, Caddy), route /mcp to the MCP container on port 8091. Example for Traefik labels:

labels:
  - traefik.enable=true
  - "traefik.http.routers.layers-mcp.rule=Host(`YOUR_DOMAIN`) && PathPrefix(`/mcp`)"
  - traefik.http.routers.layers-mcp.priority=200
  - traefik.http.services.layers-mcp.loadbalancer.server.port=8091

With nginx:

location /mcp {
    proxy_pass http://layers-mcp:8091;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 300s;
    proxy_buffering off;
}
⁠Standalone Docker

If you want to run the MCP server separately (not in the Layers Docker network):

docker run -d \
  --name layers-mcp \
  -p 8091:8091 \
  -e LAYERS_BACKEND_BASEURL=https://YOUR_DOMAIN \
  -e LAYERS_BACKEND_FORWARDINCOMINGAUTH=true \
  -e LAYERS_BACKEND_REQUIREINCOMINGAUTH=true \
  sinups/layers-mcp-server:latest
⁠Single-User Mode

For personal use or testing, you can set a static token so clients don't need to provide their own:

docker run -d \
  --name layers-mcp \
  -p 8091:8091 \
  -e LAYERS_BACKEND_BASEURL=https://YOUR_DOMAIN \
  -e LAYERS_BACKEND_FORWARDINCOMINGAUTH=false \
  -e LAYERS_BACKEND_REQUIREINCOMINGAUTH=false \
  -e LAYERS_BACKEND_BEARERTOKEN="Bearer layers_ws_YOUR_TOKEN" \
  sinups/layers-mcp-server:latest

Then connect clients to http://localhost:8091/mcp with no Authorization header needed.


⁠Configuration Reference

Environment VariableDefaultDescription
LAYERS_BACKEND_BASEURLhttps://api.layers.mdURL of the Layers API
LAYERS_BACKEND_BEARERTOKEN(empty)Static token for single-user mode
LAYERS_BACKEND_FORWARDINCOMINGAUTHtrueForward client's Authorization header to Layers
LAYERS_BACKEND_REQUIREINCOMINGAUTHtrueReject requests missing Authorization header
LAYERS_MCP_ENDPOINT/mcpMCP endpoint path
LAYERS_MCP_LOGTOOLCALLStrueLog tool calls with arguments and duration
SERVER_PORT8091HTTP port

⁠Available Tools

73 tools grouped by what they work on. Every tool carries MCP annotations⁠ (readOnlyHint, destructiveHint, idempotentHint), so a client can ask before anything destructive happens.

This list is generated from the server's own catalogue — see scripts/render-readme-tools.mjs.

Workspaces (4 tools)
ToolDescription
layers_dashboard_activityGet activity feed for a workspace or project
layers_workspace_contextGet workspace overview: projects, members, recent pages (call first!)
layers_workspace_items_hierarchy_treeGet full workspace hierarchy (projects, folders)
layers_workspaces_listList all workspaces the token has access to
People and access (1 tool)
ToolDescription
layers_peoplewho is in the workspace, in a project, who can open a page; find one person by name or email
Projects (5 tools)
ToolDescription
layers_project_createCreate a project in a workspace
layers_project_dashboardGet project dashboard stats (task counts by status)
layers_project_getGet a project by ID
layers_project_updateRename, recolour or archive a project
layers_projects_tree_by_workspaceList all projects in a workspace
Sprints (5 tools)
ToolDescription
layers_sprint_createCreate a sprint in a project
layers_sprint_getGet a sprint by ID
layers_sprint_listList sprints of a project
layers_sprint_moveMove a sprint to another project
layers_sprint_updateRename a sprint, change its dates, or close it
Milestones (3 tools)
ToolDescription
layers_milestone_createCreate a milestone
layers_milestone_getGet a milestone by name or ID
layers_milestone_updateUpdate a milestone — name, date, description
Tasks (10 tools)
ToolDescription
layers_comment_resolveMark a comment resolved — the tick people put on a thread once it is dealt with
layers_task_assignAssign or unassign a task
layers_task_attachment_uploadAttach a file to a task — any size via a one-time upload link, so the bytes never pass through the conversation
layers_task_comment_createLeave a comment on a task — markdown is accepted and arrives formatted
layers_task_comment_updateEdit a comment on a task
layers_task_comments_listThe discussion on a task: comments with their reply threads
layers_task_createCreate a new task (auto-resolves projectName, assigneeName, statusName)
layers_task_getGet a task by ID
layers_task_list_smartSmart task list with natural-language filters (overdue, unassigned, mine)
layers_task_updateUpdate task title, description, assignee, or status
Comments (1 tool)
ToolDescription
layers_commentsread the comments of a task or a page, or of many tasks at once
Tags (1 tool)
ToolDescription
layers_tag_create_or_getCreate a tag, or return the existing one with that name (safe to retry)
Pages (14 tools)
ToolDescription
layers_favorite_addAdd a page, project or folder to the person's favourites
layers_favorite_removeRemove a page, project or folder from the person's favourites
layers_page_appendAppend content to the end of a page without rewriting it
layers_page_comment_createLeave a comment on a page — markdown is accepted and arrives formatted
layers_page_comments_listThe discussion on a page: comments with their reply threads
layers_page_createCreate a new page with rich HTML content
layers_page_duplicateDuplicate a page (with sub-pages)
layers_page_getGet a page by ID
layers_page_list_by_workspaceList pages across the workspace
layers_page_moveMove a page to a different folder or project
layers_page_settingsRead a page's settings — lock, width, pin, visibility
layers_page_shareShare a page with a person by email
layers_page_updateUpdate a page; pass the version you read to avoid overwriting someone else's edit
layers_page_visibilityChange who can see a page
Folders (4 tools)
ToolDescription
layers_folder_contentsList contents of a folder (pages, subfolders)
layers_folder_createCreate a folder inside a project or another folder
layers_folder_getGet a folder by ID
layers_folder_updateRename or move a folder
Attachments (1 tool)
ToolDescription
layers_attachment_downloadprivate one-time link to download an attachment of a task, page or Disk file
Disk (files) (1 tool)
ToolDescription
layers_disklist files and folders on the Disk — My files, Shared with me, or a folder by path
Forms (7 tools)
ToolDescription
layers_form_briefTurn a plain-language brief into a form draft
layers_form_createCreate a new form
layers_form_getGet a form by ID
layers_form_listList forms, including forms inside projects
layers_form_media_searchFind images usable as form backgrounds
layers_form_publishPublish a form and get its public link
layers_form_updateUpdate form fields or settings
Favourites, reference, form results (3 tools)
ToolDescription
layers_favoriteswhat is in the person's favourites
layers_form_resultsa form's answers, or its counts and completion rate
layers_referencereference for composing: page blocks, form themes, form templates, form CSS classes
Search (1 tool)
ToolDescription
layers_searchFull-text search across tasks, pages, and projects
Navigation (1 tool)
ToolDescription
layers_navigateWalk the hierarchy one level at a time: what is inside this thing?
Users (1 tool)
ToolDescription
layers_user_meGet the currently authenticated user
Membership (5 tools)
ToolDescription
layers_invitation_acceptAccept a workspace invitation
layers_invitation_deleteRevoke a pending invitation
layers_workspace_inviteInvite a person to a workspace by email, with a role
layers_workspace_kickRemove a member from a workspace
layers_workspace_usage_summarySeats, storage and plan limits for a workspace
Delete and restore (2 tools)
ToolDescription
layers_deletedelete a task, page, project, list, folder, form, milestone, tag, comment or attachment — to the trash
layers_restorebring any of them back from the trash
Bulk operations (2 tools)
ToolDescription
layers_task_bulk_createCreate many tasks in one call
layers_task_bulk_updateUpdate many tasks in one call
Tool slices (1 tool)
ToolDescription
layers_toolsetsList the tool slices this server can expose, and their token cost

⁠Smart Features

Workspace Context — Call layers_workspace_context first to get a quick overview of projects, members, and recent pages. The AI assistant will know exactly where to create content.

Name Resolution — Use human-readable names instead of UUIDs:

"Create a task in project Design v2 and assign to Alice"

The server resolves projectName, assigneeName, statusName, and priorityName automatically.

Disambiguation — When a name matches multiple items, the server returns a helpful list:

Multiple projects match 'MCP'. Please be more specific:
  - MCP Testing  (id=b0f02a3d...)
  - MCP Integration Test  (id=dc399ea8...)
Use the exact name or provide the ID directly.

Auto-defaults — Missing fields get smart defaults:

  • Status → first status in project (usually "Backlog")
  • Priority → default priority (usually "Normal")
  • Sprint → latest sprint, or auto-creates "Task List"
  • Task type → "Task"

⁠Usage Examples

Browse your workspace

"What workspaces do I have access to?"

Find overdue work

"Show me all tasks that are overdue"

Create a task

"Create a task called 'Fix login bug' in project Alpha and assign it to me"

Read a document

"Read the PRD page"

Check project health

"What's the progress on the Mobile App project?"

Plan a sprint

"List all unassigned tasks in the Backend project"

Manage team

"Who are the members of the Design project?"


⁠Troubleshooting

⁠Server not appearing in client
  1. Verify JSON syntax: cat your-config.json | python3 -m json.tool
  2. Check Node.js: node --version (need 18+)
  3. Fully restart the client — Cmd+Q (macOS) / Alt+F4 (Windows), not just close the window
  4. nvm users: Use the full path to npx (see Claude Desktop section above)
⁠401 Unauthorized on tool calls

Symptom: Server connects, lists 73 tools, but tool calls return 401.

Cause: Your token is from a different Layers instance than the MCP server URL.

Fix: Make sure the MCP server URL and the token are from the same Layers instance. A token created on workspace-a.layers.md will not work with an MCP server pointing to workspace-b.layers.md.

⁠Token expired or invalid

Symptom: 401 Unauthorized on all requests.

Fix: Create a new token in Settings > Access Tokens. Tokens start with layers_ws_ and are shown only once.

⁠Everything reads fine, but nothing can be changed

Symptom: 403 with TOKEN_SCOPE_READ_ONLY on any create / update / assign / comment / delete, while listing and search keep working.

Fix: The token was issued for reading only. Access level is chosen once, at creation, and cannot be changed afterwards — issue a new token in Settings > Access Tokens with Access: read and write and swap it into your client config. Retrying, or reaching for a different tool, will not help.

⁠OAuth popup appears (mcp-remote)

Symptom: Browser opens asking for OAuth login instead of using the token.

Fix: Clear the mcp-remote auth cache and restart:

rm -rf ~/.mcp-auth
⁠Timeout on startup

Fix: Set a longer timeout with the MCP_TIMEOUT environment variable:

{
  "mcpServers": {
    "layers": {
      "command": "npx",
      "args": ["mcp-remote", "https://YOUR_DOMAIN/mcp", "--header", "Authorization:Bearer YOUR_TOKEN"],
      "env": {
        "MCP_TIMEOUT": "10000"
      }
    }
  }
}
⁠Behind a corporate VPN

Add your CA certificate:

"env": {
  "NODE_EXTRA_CA_CERTS": "/path/to/corporate-ca.crt"
}
⁠Enable debug logging (mcp-remote)
npx mcp-remote https://YOUR_DOMAIN/mcp --debug

Logs are written to ~/.mcp-auth/.


⁠How It Works

AI Assistant (Claude / Cursor / Windsurf / VS Code)
        |
        |  MCP Streamable HTTP (2025-06-18)
        |  Authorization: Bearer layers_ws_...
        v
+------------------------------+
|      Layers MCP Server       |  :8091/mcp
|  Java 17 + Spring Boot 4     |
|  73 tools, 10 resources      |
|  8 prompts, 8 completions    |
+-------------+----------------+
              |  REST API + Bearer token (forwarded)
              v
+------------------------------+
|        Layers API            |  your-domain.com
+------------------------------+

Multi-user mode (default): Client token -> MCP Server -> Layers API (forwarded as-is)

Single-user mode: MCP Server static token -> Layers API (client sends no token)


⁠Build from Source

./mvnw -DskipTests package
java -jar target/layers-mcp-server.jar \
  --spring.config.location=file:./application-external.properties \
  --spring.profiles.active=server

See deploy/application-external.properties.example for configuration template.


⁠Docker Hub

docker pull sinups/layers-mcp-server:latest

Available for linux/amd64 and linux/arm64.


Tag summary

Content type

Image

Digest

sha256:12e65ea37…

Size

143.1 MB

Last updated

about 3 hours ago

docker pull sinups/layers-mcp-server