figjam-context-mcp 0.0.0-stage → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +70 -0
- package/CHANGELOG.md +58 -0
- package/LICENSE +21 -0
- package/README.md +150 -2
- package/SECURITY.md +26 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/boardDiff.d.ts +55 -0
- package/dist/lib/boardDiff.js +285 -0
- package/dist/lib/boardDiff.js.map +1 -0
- package/dist/lib/cache.d.ts +9 -0
- package/dist/lib/cache.js +63 -0
- package/dist/lib/cache.js.map +1 -0
- package/dist/lib/connectorGraph.d.ts +32 -0
- package/dist/lib/connectorGraph.js +121 -0
- package/dist/lib/connectorGraph.js.map +1 -0
- package/dist/lib/docStructureMapper.d.ts +24 -0
- package/dist/lib/docStructureMapper.js +121 -0
- package/dist/lib/docStructureMapper.js.map +1 -0
- package/dist/lib/env.d.ts +2 -0
- package/dist/lib/env.js +25 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib/evidence.d.ts +63 -0
- package/dist/lib/evidence.js +474 -0
- package/dist/lib/evidence.js.map +1 -0
- package/dist/lib/figmaApi.d.ts +28 -0
- package/dist/lib/figmaApi.js +180 -0
- package/dist/lib/figmaApi.js.map +1 -0
- package/dist/lib/llmClient.d.ts +65 -0
- package/dist/lib/llmClient.js +459 -0
- package/dist/lib/llmClient.js.map +1 -0
- package/dist/lib/llmOutputBudgets.d.ts +6 -0
- package/dist/lib/llmOutputBudgets.js +13 -0
- package/dist/lib/llmOutputBudgets.js.map +1 -0
- package/dist/lib/llmTimeouts.d.ts +12 -0
- package/dist/lib/llmTimeouts.js +22 -0
- package/dist/lib/llmTimeouts.js.map +1 -0
- package/dist/lib/modelRegistry.d.ts +12 -0
- package/dist/lib/modelRegistry.js +91 -0
- package/dist/lib/modelRegistry.js.map +1 -0
- package/dist/lib/nodeTree.d.ts +9 -0
- package/dist/lib/nodeTree.js +174 -0
- package/dist/lib/nodeTree.js.map +1 -0
- package/dist/lib/persistentCache.d.ts +32 -0
- package/dist/lib/persistentCache.js +301 -0
- package/dist/lib/persistentCache.js.map +1 -0
- package/dist/lib/spatialCluster.d.ts +58 -0
- package/dist/lib/spatialCluster.js +340 -0
- package/dist/lib/spatialCluster.js.map +1 -0
- package/dist/lib/visionInterpreter.d.ts +14 -0
- package/dist/lib/visionInterpreter.js +207 -0
- package/dist/lib/visionInterpreter.js.map +1 -0
- package/dist/schemas/answerFromBoard.d.ts +75 -0
- package/dist/schemas/answerFromBoard.js +28 -0
- package/dist/schemas/answerFromBoard.js.map +1 -0
- package/dist/schemas/common.d.ts +14 -0
- package/dist/schemas/common.js +59 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/diagnoseLlmConfig.d.ts +35 -0
- package/dist/schemas/diagnoseLlmConfig.js +20 -0
- package/dist/schemas/diagnoseLlmConfig.js.map +1 -0
- package/dist/schemas/diffBoard.d.ts +118 -0
- package/dist/schemas/diffBoard.js +61 -0
- package/dist/schemas/diffBoard.js.map +1 -0
- package/dist/schemas/getBoardContext.d.ts +255 -0
- package/dist/schemas/getBoardContext.js +77 -0
- package/dist/schemas/getBoardContext.js.map +1 -0
- package/dist/schemas/ingestBoard.d.ts +96 -0
- package/dist/schemas/ingestBoard.js +88 -0
- package/dist/schemas/ingestBoard.js.map +1 -0
- package/dist/server.d.ts +16 -0
- package/dist/server.js +131 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/answerFromBoard.d.ts +9 -0
- package/dist/tools/answerFromBoard.js +243 -0
- package/dist/tools/answerFromBoard.js.map +1 -0
- package/dist/tools/diagnoseLlmConfig.d.ts +4 -0
- package/dist/tools/diagnoseLlmConfig.js +169 -0
- package/dist/tools/diagnoseLlmConfig.js.map +1 -0
- package/dist/tools/diffBoard.d.ts +11 -0
- package/dist/tools/diffBoard.js +49 -0
- package/dist/tools/diffBoard.js.map +1 -0
- package/dist/tools/getBoardContext.d.ts +3 -0
- package/dist/tools/getBoardContext.js +157 -0
- package/dist/tools/getBoardContext.js.map +1 -0
- package/dist/tools/ingestBoard.d.ts +8 -0
- package/dist/tools/ingestBoard.js +667 -0
- package/dist/tools/ingestBoard.js.map +1 -0
- package/dist/types.d.ts +145 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/docs/pipeline.gif +0 -0
- package/package.json +73 -4
- package/server.json +48 -0
package/.env.example
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Figma personal access token
|
|
2
|
+
# figma.com → Settings → Security → Personal access tokens
|
|
3
|
+
# Scope: file_content:read. The token must have access to the requested board.
|
|
4
|
+
# Prefer environment credentials; tool arguments can appear in client logs.
|
|
5
|
+
FIGMA_ACCESS_TOKEN=
|
|
6
|
+
|
|
7
|
+
# --- LLM (vision interpretation + answer synthesis) ---------------------
|
|
8
|
+
# OpenAI-compatible chat completions endpoint. Model quotas/prices can change.
|
|
9
|
+
# Board text/images are sent to this provider for vision and Q&A; use max_speed
|
|
10
|
+
# and get_board_context for deterministic context without model calls.
|
|
11
|
+
#
|
|
12
|
+
# 1) OpenRouter (default): create a key at https://openrouter.ai/keys.
|
|
13
|
+
# The student-free preset uses nvidia/nemotron-3-super-120b-a12b:free for
|
|
14
|
+
# text, then the openrouter/free router, and the router alone for vision.
|
|
15
|
+
# Free models and their limits change; run diagnose_llm_config to check.
|
|
16
|
+
#
|
|
17
|
+
# 2) GitHub Models: free with any GitHub account
|
|
18
|
+
# (https://github.com/marketplace/models) — set
|
|
19
|
+
# LLM_BASE_URL=https://models.github.ai/inference and use a GitHub PAT
|
|
20
|
+
# as LLM_API_KEY, with a vision-capable model id.
|
|
21
|
+
LLM_BASE_URL=https://openrouter.ai/api/v1
|
|
22
|
+
LLM_API_KEY=
|
|
23
|
+
# Supported presets: student-free
|
|
24
|
+
LLM_MODEL_PRESET=student-free
|
|
25
|
+
# Optional role-specific overrides. Legacy LLM_VISION_MODEL / LLM_TEXT_MODEL
|
|
26
|
+
# still work and become the first candidate in each list.
|
|
27
|
+
# LLM_VISION_MODELS=openrouter/free
|
|
28
|
+
# LLM_TEXT_MODELS=nvidia/nemotron-3-super-120b-a12b:free,openrouter/free
|
|
29
|
+
# LLM_FAST_TEXT_MODELS=nvidia/nemotron-3-super-120b-a12b:free,openrouter/free
|
|
30
|
+
# LLM_PROVIDER_REQUIRE_PARAMETERS=true
|
|
31
|
+
# Optional answer-only reasoning effort: none, low, medium or high.
|
|
32
|
+
# Omit to retain provider defaults; support depends on the configured model.
|
|
33
|
+
# LLM_ANSWER_REASONING_EFFORT=none
|
|
34
|
+
|
|
35
|
+
# --- Timeout controls ----------------------------------------------------
|
|
36
|
+
# Defaults are tuned so MCP UI clients do not wait forever on slow providers.
|
|
37
|
+
# Increase these only if your MCP client/request timeout is also higher.
|
|
38
|
+
# FIGMA_REQUEST_TIMEOUT_MS=15000
|
|
39
|
+
# FIGMA_FILE_REQUEST_TIMEOUT_MS=60000
|
|
40
|
+
# Global LLM request timeout; when set, it applies to roles without their own value.
|
|
41
|
+
# LLM_REQUEST_TIMEOUT_MS=20000
|
|
42
|
+
# Answers and text diagnostics: explicit role value > explicit global value > 40000 ms.
|
|
43
|
+
# answer_from_board stops after 50000 ms across all model candidates (or this value, if larger).
|
|
44
|
+
# LLM_ANSWER_REQUEST_TIMEOUT_MS=40000
|
|
45
|
+
# Vision: explicit role value > explicit global value > 60000 ms.
|
|
46
|
+
# LLM_VISION_REQUEST_TIMEOUT_MS=60000
|
|
47
|
+
# LLM_SDK_MAX_RETRIES=0
|
|
48
|
+
# LLM_RATE_LIMIT_RETRIES=1
|
|
49
|
+
# LLM_RATE_LIMIT_MAX_BACKOFF_MS=5000
|
|
50
|
+
# LLM_ANSWER_MAX_OUTPUT_TOKENS=2048
|
|
51
|
+
# LLM_VISION_MAX_OUTPUT_TOKENS=4096
|
|
52
|
+
# LLM_ANSWER_TOP_K=6
|
|
53
|
+
# LLM_ANSWER_PROMPT_MAX_CHARS=24000
|
|
54
|
+
# INGEST_BOARD_VISION_BUDGET_MS=40000
|
|
55
|
+
# INGEST_BOARD_MIN_VISION_SLOT_MS=10000
|
|
56
|
+
# INGEST_BOARD_VISION_CONCURRENCY=3
|
|
57
|
+
# FIGMA_SCREENSHOT_DOWNLOAD_CONCURRENCY=3
|
|
58
|
+
|
|
59
|
+
# --- Cache controls ------------------------------------------------------
|
|
60
|
+
# Maximum full boards retained in process memory. Older entries remain on
|
|
61
|
+
# disk and are restored on demand.
|
|
62
|
+
# FIGJAM_MCP_MEMORY_CACHE_MAX_BOARDS=10
|
|
63
|
+
|
|
64
|
+
# Private cache root (v4/ is created beneath it). Omit for the per-user OS default.
|
|
65
|
+
# macOS: ~/Library/Caches/figjam-context-mcp
|
|
66
|
+
# Windows: %LOCALAPPDATA%/figjam-context-mcp
|
|
67
|
+
# Linux: $XDG_CACHE_HOME/figjam-context-mcp or ~/.cache/figjam-context-mcp
|
|
68
|
+
# Use an absolute dedicated directory, not a shared project directory.
|
|
69
|
+
# FIGJAM_MCP_CACHE_DIR=/absolute/private/path/figjam-context-mcp
|
|
70
|
+
# Legacy v3 data is preserved; re-ingest boards to populate v4.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.4.1 — 2026-10-19
|
|
4
|
+
|
|
5
|
+
- `answer_from_board` waits up to 40 s per model request (`LLM_ANSWER_REQUEST_TIMEOUT_MS`)
|
|
6
|
+
instead of 20 s and stops after 50 s across all model candidates, so large prompts
|
|
7
|
+
on free reasoning models finish inside a 60-second client timeout. The text checks of
|
|
8
|
+
`diagnose_llm_config` use the same timeout, and timeout errors name the setting to change.
|
|
9
|
+
- Tool descriptions point overviews and whole-board summaries to `get_board_context`;
|
|
10
|
+
`answer_from_board` is for one specific question.
|
|
11
|
+
- `ingest_board` renders screenshots of several clusters per Figma request (up to
|
|
12
|
+
18 nodes) instead of one request per cluster, saving Tier 1 Figma requests.
|
|
13
|
+
- Vision summaries keep table values and statistical notation literally and name
|
|
14
|
+
a statistic such as a confidence interval only when a legend identifies it.
|
|
15
|
+
- Releases are published from CI with npm provenance.
|
|
16
|
+
- Dependency updates: `openai` 7.34, `dotenv` 18.0.8.
|
|
17
|
+
|
|
18
|
+
## 0.4.0 — 2026-10-18
|
|
19
|
+
|
|
20
|
+
- New `student-free` defaults: text uses `nvidia/nemotron-3-super-120b-a12b:free`,
|
|
21
|
+
then `openrouter/free`; vision uses `openrouter/free`. OpenRouter's
|
|
22
|
+
`provider.require_parameters` is no longer sent to other providers.
|
|
23
|
+
- `ingest_board` fits a 60-second client timeout: the vision phase defaults to
|
|
24
|
+
40 s (`INGEST_BOARD_VISION_BUDGET_MS`). Unfinished clusters continue on the
|
|
25
|
+
next ingest, and the summary says so.
|
|
26
|
+
- `diagnose_llm_config` runs its checks in parallel with the same output budgets
|
|
27
|
+
as the real calls and answers within 45 s.
|
|
28
|
+
- Stable MCP TypeScript SDK v2 and Zod 4 with unchanged tool names and required
|
|
29
|
+
fields; cancellation, ingest progress and tool annotations. Requires Node.js 22.12+.
|
|
30
|
+
- Answers cite source nodes with exact quotes and Figma links, and report
|
|
31
|
+
insufficient evidence instead of guessing.
|
|
32
|
+
- Answers keep confirmed statuses, explicit negatives, open tasks, proposals and
|
|
33
|
+
conflicting sources apart. Optional `LLM_ANSWER_REASONING_EFFORT` for answers.
|
|
34
|
+
- `get_board_context` adds pagination, character budgets, exact node lookups and
|
|
35
|
+
local BM25 search over original text, table cells and names. Topics without
|
|
36
|
+
matches return empty results.
|
|
37
|
+
- Tables, pages, sections and arrow direction are preserved. Arrows anchored to
|
|
38
|
+
the page canvas no longer create connections.
|
|
39
|
+
- `diff_board` compares the latest snapshot with an earlier one (20 are kept),
|
|
40
|
+
including table-cell, visual and connector changes.
|
|
41
|
+
- Re-ingests reuse unchanged clusters and skip the download when Figma reports
|
|
42
|
+
the same version. Model or prompt changes invalidate interpretations automatically.
|
|
43
|
+
- Ingest results report why vision is incomplete (time budget, timeout, rate
|
|
44
|
+
limit, configuration, access) and when to retry; Retry-After is respected.
|
|
45
|
+
- Private v4 cache with atomic writes and a write lock. Legacy v3 caches are kept;
|
|
46
|
+
re-ingest once after upgrading.
|
|
47
|
+
- Bounded work: 64 MiB per file, 100,000 nodes, 8 MiB per image, 64 MiB of
|
|
48
|
+
screenshots per ingest, and per-request timeouts for Figma and the LLM.
|
|
49
|
+
- Vision summaries keep visible facts and diagram direction and are marked as
|
|
50
|
+
model-derived.
|
|
51
|
+
- Offline retrieval and diff evaluation with 20 synthetic cases
|
|
52
|
+
(`npm run eval:retrieval`); MIT license and CI on Linux, macOS and Windows.
|
|
53
|
+
|
|
54
|
+
## 0.3.0 — Development history
|
|
55
|
+
|
|
56
|
+
Added board snapshot diffs, incremental interpretation, bounded concurrent vision,
|
|
57
|
+
connector relations and an npm tarball startup smoke test. No published release
|
|
58
|
+
date is asserted for this development version.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ecestnrkl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,151 @@
|
|
|
1
|
-
#
|
|
1
|
+
# figjam-context-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A local MCP server that turns FigJam boards into source-cited context for LLMs.
|
|
4
|
+
It reads a board through the Figma REST API, groups stickies, tables, images and
|
|
5
|
+
arrows into clusters, and answers questions with links back to the original nodes.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
Workshop boards hold research notes, decisions and open questions, but an LLM
|
|
12
|
+
cannot read a canvas. Screenshots and PDF exports lose tables, arrow direction
|
|
13
|
+
and the link to the source. This server keeps the board's structure and cites
|
|
14
|
+
every answer to the nodes it came from.
|
|
15
|
+
|
|
16
|
+
## Tools
|
|
17
|
+
|
|
18
|
+
| Tool | What it does | LLM call |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| `ingest_board` | Reads a board, clusters it and saves a local snapshot. Re-ingests reuse unchanged clusters. | In `balanced` (default) and `max_quality` mode |
|
|
21
|
+
| `get_board_context` | Returns paginated, source-linked context, optionally for a topic or exact node IDs. Use it for overviews, summaries and documentation. | No |
|
|
22
|
+
| `answer_from_board` | Answers one specific question (a number, status or decision) from the most relevant excerpts, with node-level citations or an insufficient-evidence result. Not meant for whole-board summaries. | Yes |
|
|
23
|
+
| `diff_board` | Compares two snapshots: new, removed and changed clusters, nodes, table cells and arrows. | No |
|
|
24
|
+
| `diagnose_llm_config` | Tests the configured models with small text and image JSON checks. | Yes |
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
Requires Node.js 22.12 or later.
|
|
29
|
+
|
|
30
|
+
1. Create a Figma personal access token under **Settings → Security → Personal
|
|
31
|
+
access tokens** with the scope `file_content:read`. Add `file_metadata:read`
|
|
32
|
+
so re-ingests of an unchanged board skip the download. The token's account
|
|
33
|
+
needs access to the board.
|
|
34
|
+
2. Optional: create an OpenRouter key at
|
|
35
|
+
[openrouter.ai/keys](https://openrouter.ai/keys). It enables image
|
|
36
|
+
interpretation, `answer_from_board` and `diagnose_llm_config`. Without it, use
|
|
37
|
+
`ingestMode: "max_speed"` with `get_board_context` and `diff_board`.
|
|
38
|
+
3. Add the server to your MCP client:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"figjam-context": {
|
|
44
|
+
"command": "npx",
|
|
45
|
+
"args": ["-y", "figjam-context-mcp"],
|
|
46
|
+
"env": {
|
|
47
|
+
"FIGMA_ACCESS_TOKEN": "your-figma-token",
|
|
48
|
+
"LLM_BASE_URL": "https://openrouter.ai/api/v1",
|
|
49
|
+
"LLM_API_KEY": "your-openrouter-key"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For Claude Code:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
claude mcp add figjam-context -e FIGMA_ACCESS_TOKEN=your-figma-token -e LLM_BASE_URL=https://openrouter.ai/api/v1 -e LLM_API_KEY=your-openrouter-key -- npx -y figjam-context-mcp
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then ask your assistant to ingest a board URL, for example *"Ingest
|
|
63
|
+
https://www.figma.com/board/FILE_KEY/Name and list the open questions."*
|
|
64
|
+
The `boardId` of every later call is the file key from that URL.
|
|
65
|
+
|
|
66
|
+
All settings: [configuration](https://github.com/ecestnrkl/figjam-mcp/blob/main/docs/configuration.md).
|
|
67
|
+
Problems: [troubleshooting](https://github.com/ecestnrkl/figjam-mcp/blob/main/docs/troubleshooting.md).
|
|
68
|
+
|
|
69
|
+
## How it works
|
|
70
|
+
|
|
71
|
+
1. **Read.** `ingest_board` downloads the board through the Figma REST API. If
|
|
72
|
+
Figma reports the same version as the last ingest, the download is skipped.
|
|
73
|
+
2. **Normalize.** Nodes become text, table cells, images and connectors, with
|
|
74
|
+
their pages and sections.
|
|
75
|
+
3. **Cluster.** Nearby nodes are grouped by rotation-aware distance. Arrows
|
|
76
|
+
between groups become directed relations.
|
|
77
|
+
4. **Interpret.** In `balanced` mode, clusters with images or little text get a
|
|
78
|
+
vision-model summary within a 40-second budget; the rest keep their extracted
|
|
79
|
+
text. Unfinished clusters continue on the next ingest.
|
|
80
|
+
5. **Serve.** The snapshot is saved to a private local cache.
|
|
81
|
+
`get_board_context` searches it (BM25 over original text, table cells and
|
|
82
|
+
names), `answer_from_board` answers with citations and `diff_board` compares
|
|
83
|
+
snapshots.
|
|
84
|
+
|
|
85
|
+
## Models and cost
|
|
86
|
+
|
|
87
|
+
The default preset `student-free` uses free OpenRouter models: text goes to
|
|
88
|
+
`nvidia/nemotron-3-super-120b-a12b:free` and then `openrouter/free`; images go to
|
|
89
|
+
`openrouter/free`, which picks a free model that accepts images and structured
|
|
90
|
+
outputs. The free default is best effort: free models change, hit rate limits
|
|
91
|
+
and time out.
|
|
92
|
+
|
|
93
|
+
- OpenRouter allows free models 20 requests per minute and 50 requests per day,
|
|
94
|
+
or 1,000 per day once you have bought at least 10 credits
|
|
95
|
+
([limits](https://openrouter.ai/docs/api-reference/limits)).
|
|
96
|
+
- Figma limits file downloads and image renders by seat and plan: Dev and Full
|
|
97
|
+
seats on paid plans get 10–20 requests per minute, View and Collab seats and
|
|
98
|
+
files in Starter plans at most 20 per month
|
|
99
|
+
([rate limits](https://developers.figma.com/docs/rest-api/rate-limits/)).
|
|
100
|
+
One render request covers up to 18 screenshots (about three clusters), so a
|
|
101
|
+
default ingest usually needs at most four; `max_speed` needs none.
|
|
102
|
+
- For a team, configure your own model: `LLM_BASE_URL` takes any
|
|
103
|
+
OpenAI-compatible endpoint, and `LLM_TEXT_MODELS` / `LLM_VISION_MODELS` set
|
|
104
|
+
the model lists.
|
|
105
|
+
- Run `diagnose_llm_config` after every model change. It checks JSON replies
|
|
106
|
+
and image understanding with synthetic inputs.
|
|
107
|
+
|
|
108
|
+
## Privacy
|
|
109
|
+
|
|
110
|
+
- The server reads from Figma and never changes a board.
|
|
111
|
+
- `balanced` and `max_quality` ingests send board text and screenshots to the
|
|
112
|
+
configured LLM provider. `answer_from_board` sends the question and selected
|
|
113
|
+
excerpts. `max_speed` with `get_board_context` and `diff_board` sends nothing
|
|
114
|
+
to an LLM provider.
|
|
115
|
+
- Snapshots stay in a private per-user cache on your machine. Delete the cache
|
|
116
|
+
directory to remove them.
|
|
117
|
+
- Keep tokens in the client's environment. A token passed as a tool argument can
|
|
118
|
+
appear in client logs.
|
|
119
|
+
|
|
120
|
+
## Limitations
|
|
121
|
+
|
|
122
|
+
- Image interpretation gets 40 seconds per ingest by default; large boards need
|
|
123
|
+
several ingests to interpret every image cluster.
|
|
124
|
+
- A client that cancels an ingest, for example after a 60-second timeout, gets
|
|
125
|
+
no partial result.
|
|
126
|
+
- Free models are best effort. Citations show where an answer comes from, not
|
|
127
|
+
that its conclusion is right.
|
|
128
|
+
- Search is lexical; synonyms without shared words do not match.
|
|
129
|
+
- Local stdio only; no hosted or multi-user mode.
|
|
130
|
+
|
|
131
|
+
Measured results: [evaluation](https://github.com/ecestnrkl/figjam-mcp/blob/main/docs/evaluation.md).
|
|
132
|
+
|
|
133
|
+
## Development
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
git clone https://github.com/ecestnrkl/figjam-mcp.git
|
|
137
|
+
cd figjam-mcp
|
|
138
|
+
npm ci
|
|
139
|
+
cp .env.example .env # add your tokens
|
|
140
|
+
npm run check # typecheck, tests, build, package validation
|
|
141
|
+
npm run package:smoke # install the packed package and exercise all tools
|
|
142
|
+
npm run eval:retrieval # offline retrieval and diff evaluation
|
|
143
|
+
npx @modelcontextprotocol/inspector --config inspector.config.json
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
See [CONTRIBUTING.md](https://github.com/ecestnrkl/figjam-mcp/blob/main/CONTRIBUTING.md)
|
|
147
|
+
and the [changelog](https://github.com/ecestnrkl/figjam-mcp/blob/main/CHANGELOG.md).
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
[MIT](https://github.com/ecestnrkl/figjam-mcp/blob/main/LICENSE) © 2026 ecestnrkl
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
Security fixes target the latest release on the current development line.
|
|
4
|
+
Use supported Node.js LTS versions and apply dependency updates promptly.
|
|
5
|
+
|
|
6
|
+
## Reporting
|
|
7
|
+
|
|
8
|
+
Use GitHub's **Report a vulnerability** option on the repository Security tab
|
|
9
|
+
when private reporting is enabled. If that option is unavailable, open an issue
|
|
10
|
+
requesting a private reporting channel without including exploit details or
|
|
11
|
+
secrets. Do not attach Figma tokens, provider keys, private board content or cache
|
|
12
|
+
files to public issues. There is no guaranteed response-time SLA.
|
|
13
|
+
|
|
14
|
+
## Data boundary
|
|
15
|
+
|
|
16
|
+
This is a local stdio server intended for one user's trusted MCP client. It does
|
|
17
|
+
not expose an HTTP endpoint or implement multi-user access control. Figma tokens
|
|
18
|
+
belong in the environment. Board text/screenshots and questions can be sent to the
|
|
19
|
+
configured LLM provider; provider retention and costs depend on that provider.
|
|
20
|
+
Use max_speed ingest and get_board_context to avoid model calls.
|
|
21
|
+
|
|
22
|
+
Local snapshots contain board content and are stored in a private per-user cache.
|
|
23
|
+
Only connect clients trusted to read that data. Re-ingest after upgrading from
|
|
24
|
+
legacy cache formats. Stop the server before removing its cache directory.
|
|
25
|
+
Board content is untrusted input; source citations and prompt boundaries reduce
|
|
26
|
+
risk but do not make model output authoritative. Review cited source nodes.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
4
|
+
import { createServer, packageMetadata } from "./server.js";
|
|
5
|
+
async function main() {
|
|
6
|
+
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
7
|
+
process.stdout.write(`${packageMetadata.version}\n`);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const handle = serveStdio(() => createServer());
|
|
11
|
+
const shutdown = () => { void handle.close(); };
|
|
12
|
+
process.once("SIGINT", shutdown);
|
|
13
|
+
process.once("SIGTERM", shutdown);
|
|
14
|
+
console.error(`${packageMetadata.name} running on stdio`);
|
|
15
|
+
}
|
|
16
|
+
main().catch((error) => {
|
|
17
|
+
console.error(`Fatal error starting ${packageMetadata.name}:`, error);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE5D,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,eAAe,CAAC,OAAO,IAAI,CAAC,CAAC;QACrD,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,YAAY,EAAE,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACjC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAClC,OAAO,CAAC,KAAK,CAAC,GAAG,eAAe,CAAC,IAAI,mBAAmB,CAAC,CAAC;AAC5D,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,KAAK,CAAC,wBAAwB,eAAe,CAAC,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { BoardData } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Compares two ingest snapshots of the same board — "what changed since the
|
|
4
|
+
* last workshop?". Works on three levels:
|
|
5
|
+
* - nodes: added / removed / edited content nodes (structural containers
|
|
6
|
+
* and connectors excluded — connectors are diffed separately),
|
|
7
|
+
* - clusters: matched across snapshots by member overlap, then classified
|
|
8
|
+
* as unchanged (same content hash), modified, new, or removed,
|
|
9
|
+
* - connections: connector arrows diffed as (from, to, label) edges and
|
|
10
|
+
* rendered with the owning clusters' labels.
|
|
11
|
+
*/
|
|
12
|
+
export interface ClusterSummaryRef {
|
|
13
|
+
label: string;
|
|
14
|
+
summary: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ModifiedClusterDiff {
|
|
17
|
+
label: string;
|
|
18
|
+
previousLabel: string;
|
|
19
|
+
addedNodeCount: number;
|
|
20
|
+
removedNodeCount: number;
|
|
21
|
+
editedNodeCount: number;
|
|
22
|
+
}
|
|
23
|
+
export interface BoardDiffStats {
|
|
24
|
+
addedNodes: number;
|
|
25
|
+
removedNodes: number;
|
|
26
|
+
editedNodes: number;
|
|
27
|
+
newClusters: number;
|
|
28
|
+
removedClusters: number;
|
|
29
|
+
modifiedClusters: number;
|
|
30
|
+
unchangedClusters: number;
|
|
31
|
+
addedConnections: number;
|
|
32
|
+
removedConnections: number;
|
|
33
|
+
}
|
|
34
|
+
export interface BoardDiffResult {
|
|
35
|
+
stats: BoardDiffStats;
|
|
36
|
+
newClusters: ClusterSummaryRef[];
|
|
37
|
+
removedClusters: ClusterSummaryRef[];
|
|
38
|
+
modifiedClusters: ModifiedClusterDiff[];
|
|
39
|
+
addedConnections: string[];
|
|
40
|
+
removedConnections: string[];
|
|
41
|
+
summaryText: string;
|
|
42
|
+
tableCellChanges: TableCellChange[];
|
|
43
|
+
}
|
|
44
|
+
export interface TableCellChange {
|
|
45
|
+
tableNodeId: string;
|
|
46
|
+
cellId: string;
|
|
47
|
+
changeType: "added" | "removed" | "modified";
|
|
48
|
+
previousText?: string;
|
|
49
|
+
currentText?: string;
|
|
50
|
+
previousRow?: number;
|
|
51
|
+
previousColumn?: number;
|
|
52
|
+
currentRow?: number;
|
|
53
|
+
currentColumn?: number;
|
|
54
|
+
}
|
|
55
|
+
export declare function diffBoards(baseline: BoardData, current: BoardData): BoardDiffResult;
|