Notion MCP server returns 401 invalid_token from a custom client: correct auth flow?
I am trying to use the remote Notion MCP server (https://mcp.notion.com/mcp (opens external site)) from a custom Python client with fastmcp. I send an internal integration token in the Authorization header and get 401 Unauthorized with {"error":"invalid_token","error_description":"Invalid token format"}. Other users see the same with the SSE endpoint and with OAuth access tokens from a public Notion integration. What is the correct way to authenticate a programmatic MCP client?
Originally asked on GitHub: https://github.com/makenotion/notion-mcp-server/issues/106
5 answers
Status: still open, but a Notion collaborator explained the cause. The remote MCP server does not accept an internal integration token, and it also does not accept an access token obtained directly from a public Notion integration. Your MCP client must start the OAuth flow against the MCP server endpoint itself (https://mcp.notion.com/mcp (opens external site)) and then use the token that this flow returns.
The same 401 invalid_token / Invalid token format appears whether you call the streamable-http endpoint (https://mcp.notion.com/mcp (opens external site)), the SSE endpoint (https://mcp.notion.com/sse (opens external site)), send an internal integration token, or send an ntn_ token acquired from a public Notion integration OAuth flow. The Notion-Version header value (2022-06-28 or 2025-09-03) does not change the result.
For a custom client, the collaborator pointed to Notion's "Get started with MCP" docs at https://developers.notion.com/docs/get-started-with-mcp (opens external site) and recommended using the MCP Inspector at https://modelcontextprotocol.io/legacy/tools/inspector (opens external site) to connect to the remote server while developing. In practice, do not try to put a pre-obtained token into an Authorization header; instead, implement or reuse an MCP OAuth client that authorizes against the MCP server and then uses the returned token for requests.
The thread does not contain a complete working Python or TypeScript snippet, so treat the implementation guidance as general advice and verify the exact OAuth flow with the Inspector.
Did this work for you?What a GitHub user tried on GitHub (Didn't work)
Used fastmcp Client with an internal integration token in the Authorization header and Notion-Version 2022-06-28 against https://mcp.notion.com/mcp (opens external site). Got httpx.HTTPStatusError: 401 Unauthorized.
Source: https://github.com/makenotion/notion-mcp-server/issues/106 (opens external site)
Did this work for you?What @anwendo tried on GitHub (Didn't work)
Tried the SSE endpoint and got 401 with the error body {"error":"invalid_token","error_description":"Invalid token format"}.
Did this work for you?What @djyde tried on GitHub (Didn't work)
Passed an ntn_ token in the Authorization header but still got 401 with the error body invalid_token / Invalid token format.
Did this work for you?What @Mouse-Coach tried on GitHub (Didn't work)
Completed OAuth 2.0 with a new public Notion integration to get an ntn_ access token, then used it as Bearer in a TypeScript StreamableHTTPClientTransport against https://mcp.notion.com/mcp (opens external site) with Notion-Version 2025-09-03 and then 2022-06-28. Both attempts returned HTTP 401 invalid_token.
Did this work for you?