Atlas / MCP servers / grey-iris / Easy Notion

Easy NotionBLOCK

mcp/grey-iris/easy-notion

Markdown-first Notion MCP server. ~6-7x fewer response tokens vs official Notion MCP. 43 tools.

Verdict
BLOCK
Grade
D
Trust score
63 /100
Exposed tools
38 22r · 14w · 2d
Transport
stdio · streamable-http
License
MIT
Stars
53
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

Markdown-first MCP server that connects AI agents to Notion. Agents write markdown — easy-notion-mcp converts it to Notion's block API and back again.

43 tools · 24 block types · ~6–7× fewer response tokens vs official Notion MCP · Documented round-trip support

[](https://www.npmjs.com/package/easy-notion-mcp) [](LICENSE) [](package.json) [](https://discord.gg/S8cghJSVBU) [](https://glama.ai/mcp/servers/Grey-Iris/easy-notion-mcp)

npx easy-notion-mcp

[See it in action →](https://www.notion.so/easy-notion-mcp-327be876242f817f9129ff1a5a624814) Live Notion page created and managed entirely through easy-notion-mcp.

Contents: Comparison · Setup · CLI profiles · Config · Why markdown · How it works · Tools · MCP resources · Block types · Round-trip · Databases · Cookbook · Security · Stability · FAQ · Community

How does easy-notion-mcp compare to other Notion MCP servers?

Read from source at commit 1f6a2a352f58OBSERVED · 2026-10-08
02

Connect

Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.

claude-code (npm)
claude mcp add easy-notion-mcp --env NOTION_TOKEN=${NOTION_TOKEN} -- npx -y [email protected]
03

Exposed tools (38)

22 read · 14 write · 2 destructive. Blast radius: 2 tools can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.

ToolRiskDescription
add_commentwriteAdd a comment to a page. Supports inline markdown and page mentions with @[Title](notion-url). Unlike append_content, a mention the integration cannot resolve is not downgraded to a plain link and returns no warning, so the call can fail. Returns { id, content }.
add_database_entrieswriteCreate multiple entries in a database in one call. Each entry uses the same simple key-value format as add_database_entry. Returns per-entry results \u2014 partial failures don
archive_pagereadArchive a page in Notion.
create_databasewriteCreate a database under a parent page. Supported property types and extras: - title - rich_text (alias: text) - number (optional: format, for example
create_pagewriteCreate a page
create_page_from_filewriteCreate a Notion page from a local markdown file. The server reads and validates the file, then creates the same result as create_page without sending file contents through the agent context. The server converts the markdown to native Notion blocks (not flat text) and automatically handles Notion
create_viewwriteCreate a Notion database view. Pass database_id. Dashboard views and dashboard widget placement are not supported.
delete_database_entrydestructiveDelete (archive) a database entry.
delete_viewdestructiveDelete a Notion database view. Destructive: confirm must be exactly true.
fake_toolreadFake
find_replacereadFind and replace text on a page. Preserves uploaded files and blocks that aren
get_configreadReport this server
get_databasereadGet a database
get_mereadGet the current bot user.
get_viewreadRetrieve one Notion database view by ID. Returns a curated summary (id, name, type, url, data_source_id); set include_config: true for the full raw Notion view object.
list_commentsreadList comments on a page.
list_databasesreadList all databases the integration can access. Returns database names and IDs \u2014 use get_database on any result to see its schema.
list_pagesreadList child pages under a parent page. Each row returns id, title, created_time, and last_edited_time. Timestamps are full ISO-8601 values from Notion, rounded to the minute, and last_edited_time advances on page content and property edits.
list_usersreadList workspace users.
list_viewsreadList Notion database views. Pass exactly one of database_id or data_source_id. Returns a curated summary of each view (id, name, type, url, data_source_id) plus pagination cursors; set include_config: true for the full raw Notion view objects.
markdown-conventionsreadSupported markdown syntax for page creation, appends, replacements, targeted updates, and reads.
move_pagewriteMove a page to a new parent page.
property-paginationreadHow read_page and query_database paginate long Notion property values.
query_viewreadQuery a Notion database view. Creates a temporary view query, fetches database row results, then deletes the query.
read_pagereadRead a page
replace_contentreadReplaces all page content with the provided markdown atomically (one Notion API call). Notion
restore_pagereadRestore an archived page.
restore_togglereadRestore an archived toggle or toggleable heading by archived block ID. Use the block ID returned by archive_toggle; Notion does not expose archived child enumeration for title search or read_page include_archived.
searchreadSearch Notion pages or databases. Use filter:
search_in_pagereadSearch raw Notion block plain text inside a page, optionally scoped to one toggle or toggleable heading by title. Matching is case-insensitive plain substring search.
share_pagereadReturn the page URL that can be shared from Notion.
update-data-source-guidewriteFull-list schema semantics, raw/helper payload modes, and examples for update_data_source.
update_blockwriteUpdate a single block in place by ID. Preserves the block
update_data_sourcewriteCRITICAL: full-list semantics. When you update a select or status property
update_pagewriteUpdate page title, icon, or cover. Cover accepts an image URL, or a file:// path (stdio transport only) which will be uploaded to Notion. In HTTP transport, the file:// form is rejected — use an HTTPS URL instead.
update_sectionwriteDESTRUCTIVE, no rollback: this tool deletes blocks in the section, then writes new blocks. If the write fails mid-call, the section is left partially or fully emptied; for most sections the heading anchor is deleted, so a retry can fail with
update_viewwriteUpdate a Notion database view. Pass at least one update field. Null filter, sorts, or quick_filters values are forwarded to clear those fields.
warning-shapeswriteWarning codes and response shapes emitted by markdown read and write tools.
04

Trust audit

BLOCKgrade D · trust 63/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)FAIL
L3Class-specific surfaceWARN
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
declared (10 observation(s))
Network
declared (5 observation(s))
Shell
declared (1 observation(s))
Dependencies
not all pinned
Secrets in source
found

Findings (25)

HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/cli/init.ts:36
exec(cmd: string, args: string[]): Promise<{ code: number; stdout: string; stderr: string }>;
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHPrompt injection · prompt.zero_width · CWE-94, CWE-1427
.meta/research/test-gap-frame-2-roundtrip-2026-04-20.md:58
| BOM prefix | `"# Title"` | BOM becomes part of first line. `marked.lexer` may handle it (marked does strip BOM per GFM spec). | Likely handled by marked. **Needs verification test.** |
Why it matters. invisible characters in instruction text
Fix. strip non-printing characters
HIGHPrompt injection · prompt.zero_width · CWE-94, CWE-1427
.meta/research/test-gap-frame-2-roundtrip-2026-04-20.md:60
| Zero-width joiner | `"Helloworld"` | Passes through as content in `createRichText`. `marked` treats as text. | Round-trips cleanly (invisible char preserved). Notion may or may not strip it server-s
Why it matters. invisible characters in instruction text
Fix. strip non-printing characters
MEDIUMFilesystem / path · fs.system_paths · CWE-22, CWE-59
src/cli/init.ts:237
"/usr/local/bin/claude",
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
tests/get-config.test.ts:220
const secret = "ntn_supersecrettokenvalue123456";
MEDIUMFilesystem / path · mcp.destructive_tools · CWE-22, CWE-59
delete_database_entry, delete_view
Why it matters. 2 tool(s) can delete or overwrite
Fix. prefer a read-only mode or scoped tokens; the page states the blast radius
LOWCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
tests/init-wizard.test.ts:37
exec(cmd: string, args: string[]): Promise<{ code: number; stdout: string; stderr: string }>;
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
scripts/bench/corpus/generate.ts:352
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../.meta/bench/corpus");
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
scripts/bench/lib/notion-provision.ts:2
import type { RichText } from "../../../src/types.js";
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
scripts/bench/lib/provision.ts:19
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
scripts/bench/pr3-live-probes.ts:21
import { NOTION_VERSION } from "../../src/notion-version.js";
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
scripts/bench/workflow-token-compare.ts:36
import { blocksToMarkdown } from "../../src/blocks-to-markdown.js";
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
.meta/audits/pr11-docker-smoke-2026-06-12.md:30
| `curl http://127.0.0.1:13333/` (default env) | **FAIL** (curl exit 56) — see Blocker B |
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
.meta/audits/pr11-docker-smoke-2026-06-12.md:50
$ docker exec ... wget -qO- http://127.0.0.1:3333/   → {"status":"ok",...}
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
.meta/audits/pr11-docker-smoke-2026-06-12.md:53
This is independent of Blocker B. Orchestrators (compose `condition: service_healthy`, k8s, swarm) will treat the container as never-ready / restart it. The HEALTHCHECK is a headline feature of this P
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
.meta/bench/pr-a1-build-report.md:171
- Server on 127.0.0.1:3333: confirmed (`curl http://127.0.0.1:3333/` → 200, `curl http://127.0.0.1:3333/mcp` → 401)
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
.meta/handoffs/2026-05-09.md:72
- **Verify:** `curl -sS http://127.0.0.1:3333/` → `{"status":"ok",...}`.
LOWSupply chain · supply.unpinned · CWE-829, CWE-1357
package.json
@modelcontextprotocol/sdk, @notionhq/client, dotenv, express, marked, @types/express, @types/node, @types/supertest
Why it matters. 13 dependency range(s) float
Fix. pin exact versions or ship a lockfile
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
.meta/audits/bench-codex-pass4-reporting-honesty.md:48
- The listing benchmark includes the npm package, but the README Option A number is not measured against npm; it is the workflow report's hosted model. The npm surface has `API-patch-page` and `API-up
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
.meta/audits/bench-codex-pass4-reporting-honesty.md:141
- The npm comparator is not pinned in the script. `scripts/bench/token-compare.ts:125-128` installs `@notionhq/notion-mcp-server@latest`, though the report records v2.3.0 at `.meta/research/token-reme
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
.meta/audits/bench-codex-pass4-reporting-honesty.md:142
- The local server is described as HEAD at `.meta/research/token-remeasure-2026-04-28.md:16`, but the report does not record the commit SHA. Current worktree HEAD during this audit is `4ef55ba6ea74ab3
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
.meta/audits/notion-api-gap-audit-2026-04-20.md:121
| `POST /v1/oauth/token` | `oauth.token` (`Client.d.ts:286`) | n/a — already used inside `auth/oauth-provider.ts`, not exposed as a tool (correct). | — | [ref](https://developers.notion.com/reference/
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
.meta/audits/notion-api-gap-audit-2026-04-20.md:122
| `POST /v1/oauth/introspect` | `oauth.introspect` (`Client.d.ts:293`) | low | Validate a token's scope/expiry. Used by OAuth-aware clients. | [ref](https://developers.notion.com/reference/introspect-
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
INFOPrompt injection · prompt.authority_framing · CWE-94, CWE-1427
.meta/audits/oauth-security-read-2026-06-13.md:90
| Scopes unenforced | If documenting the boundary suffices for 1.0: one sentence in the OAuth docs stating "a valid bearer grants the full tool surface; requested scopes are not enforced." If enforcin
INFOPrompt injection · prompt.credential_read · CWE-94, CWE-1427
.meta/audits/pre-v030-audit-b-2026-04-17.md:69
- **OAuth mode:** authenticated caller does the same, but the file lands in the caller's own workspace. Still a trust-boundary escalation from "can access your own Notion workspace" to "can read host
Why it matters. asks the agent to read credentials

Gates applied: no_behavioural_pass.

Audited 2026-10-08 · audit v0.4.1 · source sha 1f6a2a352f58full audit observations/trust-audit/mcp-server/grey-iris__easy-notion.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-081f6a2a352f58BLOCKD63first audit
06

Questions

What is the Easy Notion MCP server?

Markdown-first Notion MCP server. ~6-7x fewer response tokens vs official Notion MCP. 43 tools.

What tools does Easy Notion expose?

38 in total: 22 read-only, 14 that write, and 2 that can delete or overwrite (delete_database_entry, delete_view). Every one is listed on this page with its risk.

Is Easy Notion safe to connect to an agent?

No — not without reading the findings first. The audit graded it D (63/100) and found 3 critical or high issues in the source. Each one is listed on this page with the file and line it is on. Separately from the audit: 2 of its tools can destroy data, so scope the token you give it to what you actually need.

What credentials does Easy Notion need?

It reads ANTHROPIC_API_KEY, NOTION_OAUTH_CLIENT_ID, NOTION_OAUTH_CLIENT_SECRET, NOTION_TOKEN and OAUTH_REDIRECT_URI from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.

How does Easy Notion run?

It speaks stdio and streamable-http, so it runs as a local process your client starts. It is published on npm as easy-notion-mcp at 1.1.0.

How current is this page?

The grade is for one exact copy of the source (1f6a2a352f58), read on 2026-10-08. The repository is watched and re-audited when it changes.

Advertisement