Atlas / MCP servers / mcp-z / @mcp-z/oauth

@mcp-z/oauthCAUTION

mcp/mcp-z/oauth

Multi-account orchestration and secure token storage for OAuth-based MCP servers

Verdict
CAUTION
Grade
B
Trust score
83 /100
Exposed tools
1 1r · 0w · 0d
Transport
—
License
MIT
Stars
0
01

Overview

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

Multi-account orchestration and secure token storage for OAuth-based MCP servers.

Common uses

  • Add consistent account tools to MCP servers
  • Store OAuth tokens with a shared config and storage backend
  • Reuse the same account lifecycle across Google and Microsoft providers
  • Resolve public Client ID Metadata Documents (CIMD) safely for authorization servers

Client ID Metadata Documents

createCimdResolver() resolves a public HTTPS client_id metadata document with SSRF protections: public-unicast DNS validation pinned into the request, no redirects, a 5 KiB JSON response limit, bounded caching, and per-client request limiting. Local development can explicitly allow HTTP loopback documents with allowHttpLoopback: true.

In production, route this traffic through an egress proxy or network policy that blocks internal destinations (for example, Smokescreen-style controls). That defense composes with the resolver's application-level checks.

Install

npm install @mcp-z/oauth

Optional storage backends:

npm install keyv-duckdb
npm install keyv-file

Account tools and modes

Use AccountServer to add account tools to your MCP server.

Loopback mode (multi-account)

When using loopback OAuth, these tools are added:

  • account-me
  • account-switch
  • account-remove
  • account-list

Stateless mode (DCR/bearer)

When using stateless auth (DCR/bearer tokens), only this tool is available:

  • account-me

Example

import { AccountServer } from '@mcp-z/oauth';

const { tools, prompts } = AccountServer.createStateless({ service: 'gmail' });
// Register these tools and prompts with your MCP server.

Use createLoopback when the server manages multiple stored accounts. It requires a Keyv store, logger, and OAuth provider from your application. createStateless needs only the service name and reports the account represented by the MCP client's bearer token.

Logging helper

Use `sanitizeForL

Read from source at commit c299dd3b51e8OBSERVED · 2026-10-11
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
claude mcp add oauth --env GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET} --env JWT_PUBLIC_KEY=${JWT_PUBLIC_KEY} --env JWT_SECRET=${JWT_SECRET} --env SESSION_SECRET=${SESSION_SECRET} -- npx -y @mcp-z/[email protected]
claude-desktop
{
  "mcpServers": {
    "oauth": {
      "command": "npx",
      "args": [
        "-y",
        "@mcp-z/[email protected]"
      ],
      "env": {
        "GOOGLE_CLIENT_SECRET": "${GOOGLE_CLIENT_SECRET}",
        "JWT_PUBLIC_KEY": "${JWT_PUBLIC_KEY}",
        "JWT_SECRET": "${JWT_SECRET}",
        "SESSION_SECRET": "${SESSION_SECRET}"
      }
    }
  }
}
03

Exposed tools (1)

1 read · 0 write · 0 destructive.

ToolRiskDescription
pointsreadKey points to include
04

Trust audit

CAUTIONgrade B · trust 83/100 Install with care. The audit found things worth knowing before you trust its output.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeWARN
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

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

Findings (17)

MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
test/unit/jwt-auth.test.ts:7
const SECRET = 'test-jwt-secret-0123456789-abcdefgh';
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
test/unit/jwt-auth.test.ts:117
const token = issueToken('HS256', { secret: 'other-secret-0123456789-0123456789' });
LOWInventory / provenance · inv.hidden_file · CWE-1104
.depcheckrc
.depcheckrc
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWInventory / provenance · inv.hidden_file · CWE-1104
.ncurc.json
.ncurc.json
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/lib/account-server/loopback.ts:18
import { addAccount, getAccountInfo, getActiveAccount, getLinkedAccounts, removeAccount, setAccountInfo, setActiveAccount } from '../../account-utils.ts';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/lib/account-server/loopback.ts:19
import type { AccountInfo, McpPrompt, McpTool } from '../../types.ts';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/lib/account-server/me.ts:16
import { getAccountInfo, getActiveAccount, getToken } from '../../account-utils.ts';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/lib/account-server/me.ts:17
import type { CachedToken, McpPrompt, McpTool } from '../../types.ts';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/lib/account-server/shared-utils.ts:2
import { getAccountInfo, getLinkedAccounts } from '../../account-utils.ts';
LOWNetwork egress · net.metadata_ip · CWE-200, CWE-319
test/unit/lib/cimd-resolver.test.ts:171
for (const address of ['10.0.0.1', '172.16.0.1', '192.168.0.1', '127.0.0.1', '169.254.169.254', '0.0.0.0', '100.64.0.1', '224.0.0.1', '::1', 'fc00::1', 'fe80::1', '::ffff:127.0.0.1']) {
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
LOWNetwork egress · net.metadata_ip · CWE-200, CWE-319
test/unit/lib/cimd-resolver.test.ts:192
callback(null, [{ address: lookups === 1 ? '127.0.0.1' : '169.254.169.254', family: 4 }]);
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
test/unit/jwt-auth.test.ts:294
jwksUrl = `http://127.0.0.1:${address.port}/jwks.json`;
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
test/unit/lib/cimd-resolver.test.ts:14
await test(`http://127.0.0.1:${address.port}`);
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
test/unit/lib/cimd-resolver.test.ts:30
const clientId = `http://127.0.0.1:${req.socket.localPort}/metadata`;
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
test/unit/lib/cimd-resolver.test.ts:63
res.writeHead(302, { location: 'http://127.0.0.1/private' });
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
test/unit/lib/cimd-resolver.test.ts:131
const clientId = `http://127.0.0.1:${req.socket.localPort}/metadata`;
LOWSupply chain · supply.unpinned · CWE-829, CWE-1357
package.json
@modelcontextprotocol/server, ipaddr.js, keyv, zod, @types/mocha, @types/node, node-version-use, ts-dev-stack
Why it matters. 9 dependency range(s) float
Fix. pin exact versions or ship a lockfile

Gates applied: no_behavioural_pass.

Audited 2026-10-11 · audit v0.4.1 · source sha c299dd3b51e8full audit observations/trust-audit/mcp-server/mcp-z__oauth.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-11c299dd3b51e8CAUTIONB83first audit
06

Questions

What is the @mcp-z/oauth MCP server?

Multi-account orchestration and secure token storage for OAuth-based MCP servers

What tools does @mcp-z/oauth expose?

1 in total: 1 read-only, 0 that write, and 0 that can delete or overwrite. Every one is listed on this page with its risk.

Is @mcp-z/oauth safe to connect to an agent?

With care. The audit graded it B (83/100) and found 17 things worth knowing before you trust this server, listed below with the exact line each was found on.

What credentials does @mcp-z/oauth need?

It reads GOOGLE_CLIENT_SECRET, JWT_PUBLIC_KEY, JWT_SECRET and SESSION_SECRET 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 current is this page?

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

Advertisement