ics-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ics-mcpList my pinned notes in Architecture and show the latest diff of adr-007"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ics-mcp
Connect your AI assistant to ICS, with your own permissions
An MCP server that lets Claude, Cursor and any other MCP client read, write, version and share documents in ICS (Internal Content Sharing), a self-hosted platform for sharing team documents.
User guide · Quick start · Configuration · Tools · HTTP mode · Security · Development
You: List my pinned notes in the Architecture project, show the latest diff of adr-007 and share it with an SSO link that expires on Friday.
Assistant: calls
ics_assets_list,ics_asset_diffandics_share_create_link, then answers with the diff and the new link.
Related MCP server: Sensefold MCP server
Highlights
Acts as you, never more. Every tool call is a request to the ICS user-level API with the signed-in user's OAuth token. No shared service account and no administration tools; an administrator's token still carries that administrator's access.
53 tools with full MCP annotations for discovery, reading, Markdown and HTML writes, PDF and Word documents, versions, sharing, the relation graph and task context.
Safe destructive operations. Deletes and overwrites go through a confirmation gate: interactive elicitation, scripted
yolo(only when the operator allows it) ordry_run.Two transports.
stdiofor a local assistant with sign-in through your browser, and Streamable HTTP for remote clients such as claude.ai connectors.Token efficient. Compact responses cut to a size budget, outlines before full reads, section reads by heading, and a single call that gathers the documents of a task.
Standards first. OAuth 2.1 with PKCE S256, RFC 8707 resource indicators, RFC 9728 protected resource metadata and RS256 JWTs verified against the ICS JWKS.
Documentation
The user guide explains, for people who use ICS through an assistant, how to connect a client and sign in, what to ask for everyday tasks, how confirmations work and how to fix common errors. This README is the reference for tools and configuration.
Quick start
You need a running ICS with ICS_MCP_RESOURCE_URI set (ICS
then registers the public clients ics-mcp-stdio and ics-mcp-http) and
uv.
1. Add the server to your MCP client
Claude Desktop (claude_desktop_config.json), Claude Code (.mcp.json or claude mcp add)
and Cursor (~/.cursor/mcp.json) all accept this shape:
{
"mcpServers": {
"ics": {
"command": "uvx",
"args": ["--from", "git+https://github.com/inprojectspl/ics-mcp", "ics-mcp"],
"env": {
"ICS_BASE_URL": "https://ics.example.com",
"ICS_MCP_RESOURCE_URI": "https://ics.example.com/mcp"
}
}
}
}Pin a tag or a commit for reproducible setups, for example
git+https://github.com/inprojectspl/ics-mcp@<tag>. Restart the client after editing its
configuration.
2. Ask your assistant about ICS
The first tool call opens your browser at the ICS sign-in and consent screen. After you approve, the tokens are stored in the OS keyring and the call continues. That's it.
From a local checkout
git clone https://github.com/inprojectspl/ics-mcp && cd ics-mcp
uv sync
uv run ics-mcpIn a client configuration use "command": "uv" with
"args": ["--directory", "/path/to/ics-mcp", "run", "ics-mcp"].
PyPI is planned but not published yet.
Configuration
Every setting is an environment variable, see ics_mcp/config.py and
.env.example.
Variable | Default | Meaning |
|
| Address the server uses to reach ICS. Can be an internal address in HTTP deployments; JWKS is fetched from here. |
|
| Canonical URI of this MCP server. Must match |
|
| Public PKCE client registered in ICS. |
| same as | Public ICS URL, the |
|
|
|
|
| HTTP bind address. |
|
| HTTP port. |
|
| Stateless HTTP for several replicas, see HTTP mode. |
| empty | Comma-separated browser origins allowed besides the server's own. |
|
| Keyring service name. Use different names to keep several token profiles. |
| platform data dir, | Token file used when no OS keyring is available. |
|
| Initial confirm mode: |
|
| Whether the model may switch its own session to |
|
|
|
|
| Upload size cap, checked before files are read into memory. |
|
| Maximum number of files in one upload. |
|
| Response size budget for text payloads. |
|
| Read timeout for ICS requests, in seconds. |
|
| Write timeout for ICS requests (uploads), in seconds. |
Signing in (stdio)
sequenceDiagram
participant A as Assistant
participant M as ics-mcp
participant B as Browser
participant I as ICS
A->>M: first tool call
M->>B: open /api/auth/oauth/authorize (PKCE, resource)
B->>I: sign in and approve
I->>M: redirect to 127.0.0.1 callback with a code
M->>I: exchange the code for tokens
M->>M: store tokens in the OS keyring
M->>I: the original request, as you
M-->>A: resultThe loopback callback binds to
127.0.0.1on a random port and checksHostandstate. The flow times out after 5 minutes.Access tokens are RS256 JWTs; refresh tokens rotate. On a
401the server refreshes once and retries without the assistant noticing. Reusing a rotated refresh token revokes the whole token family in ICS.Tokens are stored in the macOS Keychain, Windows Credential Manager or libsecret under service
ics-mcp, accountdefault.Headless environments (no
$DISPLAYor$BROWSER, andCIorICS_MCP_FORCE_HEADLESSset): no browser opens and no loopback callback runs, so the tool returnsauth_requiredwith theauthorization_urland the sign-in cannot finish there. Sign in on a machine with a browser, or run the server in HTTP mode.Signing out: delete the keyring entry (see Troubleshooting). The refresh token on the ICS side expires on its own (30 days by default).
HTTP mode
With ICS_MCP_TRANSPORT=http the server speaks Streamable HTTP on /mcp, for remote clients
such as claude.ai connectors:
ICS_MCP_TRANSPORT=http \
ICS_BASE_URL=http://backend:8000 \
ICS_MCP_OAUTH_ISSUER=https://ics.example.com \
ICS_MCP_RESOURCE_URI=https://mcp.ics.example.com/mcp \
ICS_MCP_HTTP_HOST=0.0.0.0 \
uvx --from git+https://github.com/inprojectspl/ics-mcp ics-mcpA request without a valid bearer token gets
401with aWWW-Authenticateheader pointing at the RFC 9728 metadata, which names ICS as the authorization server, so clients run the OAuth flow against ICS on their own.Tokens are verified against the ICS JWKS (signature, issuer, audience, expiry), and every call to ICS carries the caller's own token. The server keeps no shared token store.
content_patharguments are refused, and a request whoseOriginis neither the server's own nor inICS_MCP_EXTRA_ORIGINSgets403.GET /healthreturns{status, version, protocol_version}without calling ICS.Sessions and their confirm mode live in process memory. For several replicas set
ICS_MCP_STATELESS=true; the confirm mode then falls back toICS_MCP_DEFAULT_CONFIRM.
Run it behind a TLS-terminating reverse proxy that does not buffer text/event-stream.
deploy/ has a Dockerfile, a compose snippet, an example Caddyfile and a
deployment guide.
Tools
53 tools. Every one declares all four MCP annotations (readOnlyHint, destructiveHint,
idempotentHint, openWorldHint) and returns structured content. Documents are addressed by
slug_or_id (a slug or a UUID), except ics_asset_undelete, which needs the UUID.
ics_help returns longer guides to the assistant.
Group | Tools |
Session and help |
|
Discovery |
|
Read |
|
Write Markdown |
|
HTML bundles |
|
PDF and Word |
|
Metadata |
|
Relation graph |
|
Tasks |
|
Versions |
|
Sharing |
|
Tools that take file content accept inline content or content_path, a local path the server
reads from disk (stdio only), which keeps large files out of the model's context.
Session and help
Tool | Purpose |
| Guides: |
| The signed-in user, scope and client. |
| Confirm mode for this session: |
| Current mode, server and protocol versions. |
Discovery
Tool | Purpose |
| Paginated list with filters, 20 per page by default and cut to the response limit. Compact items carry the summary preview, document type, freshness, maintainer, successor and estimated tokens. |
| Full-text search in Markdown, PDF text and converted Word/ODF documents. All words by default, accent-insensitive prefixes and quoted phrases; |
| Metadata only, with the full summary. |
| Files of a collection or bundle, optionally of an older version. |
| Structure (headings with |
| Projects visible to the user. |
| Share links and user grants of a document. |
Read
Tool | Purpose |
| File content: a line range, one section by heading |
| OCR state of a scanned PDF (queued, running, done, failed); |
| Rendered HTML. Expensive; prefer |
| Version history with authors. |
| Diff between two versions. |
| Read a document from a share link or token. |
Write: Markdown
Tool | Purpose |
| New single Markdown document. |
| New Markdown collection. Reports progress. |
| Change the title; the slug stays. |
| Replace a file's content as a new version. Destructive. |
| Add a file to a collection. |
| Add an empty file to a collection. |
| Move or rename a file in a collection. Destructive. |
| Delete a file from a collection. Destructive. |
| Soft-delete a document. Destructive. |
| Restore a soft-deleted document (UUID only). Share links are not restored. |
| Pin, or with |
| Replace the tag list. |
Write: HTML bundles
Tool | Purpose |
| Upload a bundle; each file has |
| Replace the whole bundle as a new version. Destructive. |
| Security scan status and findings. |
Write: PDF and Word/ODF documents
Tool | Purpose |
| Upload a local |
| Upload a new file as the next version of a |
Document metadata
Tool | Purpose |
| Document types for |
| Change metadata without a new version; |
Relation graph
Tool | Purpose |
| Relations as compact |
| Record a typed relation: |
| Change the note, or mark a changed target as checked. |
| Delete a relation. Destructive. |
| Nodes and |
| Extract links to ICS documents from the current content again and record them as |
Tasks
Tool | Purpose |
| The documents of one task in one call: required, linked, inherited from ancestors, project key documents and graph neighbours, ordered and cut to |
| Link a document to an OpenProject work package or a Project Standard node as |
| Delete a task link. Destructive. |
| Tasks a document is linked to. |
| External projects mapped to an ICS project, and its key documents. |
| Replace them (owner). Destructive; |
Versions
Tool | Purpose |
| New version replacing the whole file set. Destructive. |
| Make an older version current again. Destructive; history is kept. |
Sharing
Tool | Purpose |
| Create a link: |
| Grant a user |
| Disable a share link or revoke a user grant permanently. Destructive. |
Confirmations and destructive operations
Operations that delete or replace content, add or remove relations, unlink tasks, replace
project links, create a public share link or create a document with share_mode="public" go
through a confirmation gate. The session mode
decides what happens:
Mode | Behaviour |
| The client asks the user through MCP elicitation. A client without elicitation gets |
| No questions, for scripted or scheduled agents. The model can switch to |
| Destructive tools return a preview of what they would do and send nothing to ICS. |
The per-call confirm argument always wins: confirm=true runs without asking,
confirm=false refuses in every mode, and an omitted confirm follows the session mode.
Resources and prompts
Resource | Content |
| The first page of the user's documents. |
| Metadata plus content (single Markdown) or outline (collection, bundle). |
| The outline only. |
| One file of a collection as text. |
| An archived version: its content or its file list. |
| One file of an archived version as text. |
| Project metadata and its documents. |
Prompt | Workflow |
| Create a share link and suggest how to send it. |
| Outline, pick files, read them, summarize. |
| Turn a single Markdown document into a collection. |
Rate limits and retries
ics-mcp assumes no limits of its own and reacts to what ICS answers:
429is retried up to 3 times, waiting as long asRetry-Aftersays (at most 30 s per wait, 1 s without the header), for every method, since ICS did not process the request. When the retries run out the tool returnsrate_limited.502,503and504are retried with growing delays only forGET,HEADandOPTIONS. Writes that fail this way go back to the assistant at once, because ICS may have applied them.
For bulk work, page through ics_assets_list with cursor and filter there, and replace many
files in one ics_asset_new_version call instead of many ics_asset_update_content calls.
Security model
Scope of access: exactly what the signed-in user can do in ICS (own documents, documents shared with them, their projects), and nothing more. Tokens are never returned to the assistant;
ics_whoamishows only the client, scope and audience.Audience binding (RFC 8707): tokens are issued for this server's resource URI; in HTTP mode tokens with another
audare rejected.No token passthrough: tokens go only to ICS, which is both the authorization and the resource server.
Token storage: the OS keyring first. The fallback file is created with mode
0600and obfuscated with Fernet using a key derived from the host and user ID, which protects against accidental reading, not against code running as the same user.HTTP transport:
Originvalidation as the MCP spec requires, andcontent_pathdisabled.Logs: always on stderr (stdout carries the stdio protocol) and never with tokens; a test enforces it.
Please report vulnerabilities privately as described in SECURITY.md.
Troubleshooting
The refresh failed or the tokens were revoked. Delete the keyring entry and call a tool again:
# macOS
security delete-generic-password -s ics-mcp -a default
# Linux (libsecret)
secret-tool clear service ics-mcp username default
# Windows
cmdkey /delete:ics-mcpCheck that ICS_MCP_RESOURCE_URI is identical on the ICS and the MCP side, that
ICS_MCP_OAUTH_ISSUER matches the iss of ICS tokens, and that the client supports dynamic
client registration (or use a client pre-registered in ICS).
A 403 means the request did not come from 127.0.0.1 or had a wrong state, usually because
a proxy or VPN intercepts loopback traffic; exclude 127.0.0.1 from it. If ICS is unreachable,
check ICS_BASE_URL (with the scheme), that ICS is up, and firewalls or DNS.
Run with ICS_MCP_LOG_LEVEL=DEBUG and read the server's stderr (in Claude Desktop: Settings,
Developer, Open MCP Logs). The MCP Inspector lists the tools and lets you call them:
npx @modelcontextprotocol/inspector \
-e ICS_BASE_URL=https://ics.example.com \
-e ICS_MCP_RESOURCE_URI=https://ics.example.com/mcp \
uvx --from git+https://github.com/inprojectspl/ics-mcp ics-mcpDevelopment
git clone https://github.com/inprojectspl/ics-mcp && cd ics-mcp
uv sync --extra dev
uv run ruff check . && uv run ruff format --check .
uv run mypy --strict ics_mcp
uv run pytest --cov=ics_mcp --cov-fail-under=85The tests mock ICS with respx and use an in-memory keyring, so they need neither a running
ICS nor network access. See CONTRIBUTING.md for the workflow and
conventions, CHANGELOG.md for releases and AGENTS.md for notes
aimed at AI coding agents.
License
ics-mcp is released under the MIT License. The inProjects.ai name and logo are not covered by it, see NOTICE.
This server cannot be deployed
Maintenance
Related MCP Connectors
- KumbukaOAuthai.kumbuka
Governed, auditable knowledge your team curates for its AI assistants, self-hostable
Self-hostable shared brain for you and your AI agents — docs, flows, meetings, decisions, rationale
Persistent docs and memory for AI agents — read, write, organize & search a shared workspace.
Read, edit and improve shared documents and knowledge with people and agents.
Related MCP Servers
AlicenseAqualityAmaintenanceLets AI agents search and read a folder of Markdown documentation at query time, exposing heading-level search, whole-page or section retrieval, and page listing as tools and resources over stdio or Streamable HTTP. Can run as a CLI, a hosted endpoint, or a library inside an existing Node or Next.js site.3706 npmMIT- AlicenseNot gradedqualityBmaintenanceEnables AI clients like Claude, ChatGPT, and Cursor to search, read, and write back to one cross-model Markdown library of saved articles, videos, PDFs, notes, and conversations. Hosted remote server with OAuth 2.1 or Agent-key auth and read_only, edit, or full permission tiers.MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to create, read, update, and restore encrypted shared workspaces and artifacts addressed by a single link, so multiple agents and people can collaborate on the same living document. Writes are version-conditional to prevent clobbering, and content is encrypted client-side so the server only ever holds ciphertext.14 npmMIT
- FlicenseNot gradedqualityAmaintenanceGives AI agents access to a persistent document workspace so they can search, read, and update knowledge through 22 focused tools, 2 prompts, and 3 resources, with batch actions and direct workspace-item inspection. Runs either as a hosted Streamable HTTP endpoint with OAuth or API keys, or as a self-hosted stdio bridge pointed at your own instance.-