Permissioned MCP Server
by sn0bored
README.md
# Permissioned MCP Server
[](https://github.com/sn0bored/permissioned-mcp-server/actions/workflows/ci.yml)
A compact Model Context Protocol server that demonstrates the parts usually missing from quickstarts: explicit tool boundaries, least-privilege discovery, execution-time authorization, destructive-action confirmation, and metadata-only audit logs.
The domain is deliberately boring: a local note store. The reference is about designing a safe boundary between an AI agent and real side effects.
## What it demonstrates
- JavaScript on Node.js with the official MCP SDK v2
- Narrow read, write, and destructive tools
- Separate `notes:read`, `notes:write`, and `notes:admin` scopes
- Unauthorized tools omitted from `tools/list` and rejected again at execution
- Identifier-bound confirmation for destructive actions
- Structured tool results and MCP behavior annotations
- Audit events that never log arguments, content, or credentials
- Atomic local persistence with restrictive file permissions
- Tests for policy, handlers, persistence, and failure cases
## Run it
Requires Node.js twenty or newer.
```bash
npm install
cp .env.example .env
MCP_SCOPES=notes:read,notes:write npm start
```
Example client configuration:
```json
{
"mcpServers": {
"permissioned-notes": {
"command": "node",
"args": ["/absolute/path/to/permissioned-mcp-server/src/server.js"],
"env": {
"MCP_SCOPES": "notes:read,notes:write",
"MCP_DATA_FILE": "/absolute/path/to/notes.json",
"MCP_ACTOR": "local-agent"
}
}
}
}
```
Start read-only. Grant `notes:write` only when mutation is necessary. Keep `notes:admin` out of the default configuration.
## Tool boundary
| Tool | Scope | Side effect |
| --- | --- | --- |
| `notes.list` | `notes:read` | Returns metadata only |
| `notes.get` | `notes:read` | Reads one note |
| `notes.create` | `notes:write` | Creates one note |
| `notes.delete` | `notes:admin` | Permanently deletes one note |
See [the decision record](docs/DECISIONS.md) for the reasoning behind discovery filtering, double authorization, confirmation design, audit redaction, transport choice, and storage isolation.
## Verify it
```bash
npm run check
npm test
```
The test suite never starts a model or calls a paid API.
## Production notes
This is a local stdio reference, not a turnkey hosted authorization server. Before exposing an MCP server over Streamable HTTP, add OAuth-based authorization, token audience validation, HTTPS, rate limits, tenant isolation, durable audit storage, and client-specific consent.
## License
MIT
---
Built by [Lanier](https://lanierdev.com), an applied AI studio. More free
tools at [lanierdev.com/tools](https://lanierdev.com/tools).
TDQS
A4.1/5.0
Scored across 3 tools
Disambiguation5/5
Each tool has a clearly distinct purpose: list returns metadata, get returns a full note, and create adds a new note. No overlap in functionality.
Naming Consistency5/5
All tools follow the consistent pattern 'notes.<verb>', using simple, descriptive verbs (list, get, create). No naming style deviations.
Tool Count5/5
Three tools is a reasonable scope for a basic note management server. It covers core operations without being too sparse or excessive.
Completeness3/5
The set supports listing, reading, and creating notes but lacks update and delete operations. While a minimal viable set, these missing operations are notable gaps for typical CRUD coverage.
Maintenance
ActivitySlowing
ResponsivenessUnresponsive