Surface MCP Server
README.md
# Surface MCP Server
Model Context Protocol server for [Surface](https://tendrl.com/docs/surface/). Gives any MCP-compatible AI assistant (Claude, etc.) the ability to scan files, manage accounts, and access SDK/API documentation.
## Install
```bash
npx -y github:tendrl-inc-labs/surface-mcp
```
npm clones this repository and builds it from source on install, so there is
nothing to publish and nothing stale to serve.
Install by repository, not by name: an unrelated third party owns the name
`surface-mcp` on npm, and installing that name runs their code.
In an MCP client config:
```json
{
"mcpServers": {
"surface": {
"command": "npx",
"args": ["-y", "github:tendrl-inc-labs/surface-mcp"],
"env": {
"SURFACE_KEY": "${SURFACE_KEY}"
}
}
}
}
```
## Build from source
```bash
git clone https://github.com/tendrl-inc-labs/surface-mcp
cd surface-mcp
npm install
npm run build
```
## Configuration
Set your API key:
```bash
export SURFACE_KEY="sfk_your_token_here"
```
### Optional: Local Scanner
Point `SURFACE_SCANNER_PATH` at the Surface scanner binary to scan files locally. Files never leave your machine — the binary runs on your hardware and reports results to the server.
```bash
export SURFACE_SCANNER_PATH="/usr/local/bin/surface-scanner"
```
When this is not set, `scan_file` uploads to the API instead.
### Optional: Custom API URL
Set a custom base URL (defaults to `https://app.tendrl.com/surface/api`):
```bash
export SURFACE_BASE_URL="http://localhost:9080/api"
```
## Usage with Claude Desktop
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"surface": {
"command": "node",
"args": ["/path/to/surface/mcp-server/dist/index.js"],
"env": {
"SURFACE_KEY": "sfk_your_token_here"
}
}
}
}
```
With local scanner (files never leave your machine):
```json
{
"mcpServers": {
"surface": {
"command": "node",
"args": ["/path/to/surface/mcp-server/dist/index.js"],
"env": {
"SURFACE_KEY": "sfk_your_token_here",
"SURFACE_SCANNER_PATH": "/usr/local/bin/surface-scanner"
}
}
}
}
```
The local scanner is part of paid plans and checks its license with your key, so local mode needs `SURFACE_KEY` too (`SURFACE_API_KEY`, the scanner's own name for it, also works). The server hands the key to the scanner in its environment, never on its command line, where other processes on the machine could read it.
## Usage with Claude Code
Add to your Claude Code settings:
```bash
# API mode (uploads to server)
claude mcp add surface node /path/to/surface/mcp-server/dist/index.js -e SURFACE_KEY=sfk_your_token_here
# Local scanner mode (files stay on your machine)
claude mcp add surface node /path/to/surface/mcp-server/dist/index.js -e SURFACE_KEY=sfk_your_token_here -e SURFACE_SCANNER_PATH=/usr/local/bin/surface-scanner
```
## Tools
| Tool | Description |
|------|-------------|
| `scan_file` | Upload and scan a file for malware (accepts absolute file path) |
| `scan_payload` | Scan raw content for threats — detects prompt injection, SQL/XSS injection, credential leaks, malicious code, and suspicious tool calls. Accepts raw text (default) or base64 for binary. Max 10 MB. Takes an optional `context` for action screening (see below). |
| `get_scan` | Poll a deferred scan result by scan ID |
| `get_account` | Get account details |
| `get_usage` | Get scan usage vs monthly limit |
| `list_profiles` | List scan profiles |
| `create_profile` | Create a scan profile |
| `update_profile` | Update a scan profile |
| `delete_profile` | Delete a scan profile |
| `list_api_keys` | List API keys |
| `create_api_key` | Create an API key |
| `delete_api_key` | Delete an API key |
| `get_scan_history` | Get paginated scan history |
| `get_scan_detail` | Get full details of a historical scan |
| `get_plans` | Get available billing plans |
### Action screening context
When `scan_payload` (or `scan_bundle`) receives a tool call an agent is about to make, some actions are dangerous on their own (deleting a database, a secret in a URL, a crypto-address payout) and some only relative to you — data is only leaking if it leaves your domains or goes to a host you never declared. Pass an optional `context` object so the screener can decide confidently instead of defaulting to "Review":
```json
{
"payload": "{\"tool\":\"http_request\",\"args\":{\"method\":\"POST\",\"url\":\"https://webhook.partner.io/sync\",\"body\":{}}}",
"context": {
"principal_domains": ["acme.io"],
"allowed_egress": ["api.stripe.com", "hooks.slack.com"],
"user_request": "sync this week's tickets to our partner"
}
}
```
- **Data egress** leaving `principal_domains` (or to a free-mail address) is flagged; with `allowed_egress` set, a POST to a host on neither list is flagged for review while a Stripe or Slack call passes. A bare-IP destination or a secret in the body flags even without context.
- **Personal mailboxes** (Gmail, Outlook…) are ordinary for agents, so below strict a send to one is held only when `user_request` was passed and never named that address, the message describes bulk data, or the call came out of a document. Without `user_request` such sends are not judged — pass it to turn exfiltration screening on.
- **Dangerous on its face** — crypto and gift-card payouts, `rm -rf` of a data directory, admin grants — flag with no context.
- Build `context` from **trusted host state**, never from the payload being scanned. It is optional; omit it for face-value screening.
## Resources
| Resource | URI | Description |
|----------|-----|-------------|
| API Reference | `surface://docs/api-reference` | Complete REST API documentation |
| SDK Quick Reference | `surface://docs/sdk-overview` | Side-by-side SDK comparison |
| Webhook Guide | `surface://docs/webhooks` | Webhook setup and signature verification |
| Python SDK Docs | `surface://docs/sdk/python` | Python SDK README |
| JavaScript SDK Docs | `surface://docs/sdk/javascript` | JS/TS SDK README |
| Go SDK Docs | `surface://docs/sdk/go` | Go SDK README |
| SDK Source Files | `surface://src/sdk/{lang}/*` | SDK source code (client, models, errors, webhook) |
## Agentic Security
The `scan_payload` tool is designed for AI agent workflows. When an agent scans a payload, Surface automatically detects:
- **Prompt injection** — jailbreak attempts, role hijacking, instruction overrides
- **SQL injection** — union attacks, tautology auth bypass, blind injection
- **XSS injection** — script tags, event handlers, javascript: URIs
- **Credential exposure** — API keys, tokens, private keys, connection strings
- **Malicious code** — reverse shells, download cradles, eval/exec chains
- **Suspicious tool calls** — dangerous execute/write/http operations
- **Known malicious URLs** — checked against threat intelligence feeds
Results include `promptInjection`, `codeExtraction`, `sensitiveData`, and `toolCallAnalysis` fields with detailed findings.
## Prompts
| Prompt | Description |
|--------|-------------|
| `analyze_scan_result` | Analyze a scan result JSON and provide security assessment |
| `generate_sdk_code` | Generate SDK code for a given language and use case |
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessNo issues