Docs / Surface / ai/mcp-server
MCP Server
Surface exposes an MCP-compatible JSON-RPC endpoint so AI assistants can scan files and payloads for malware without writing HTTP client code.
There are three ways to connect:
- Local scanning (
surface-scanner --mcp): the scanner binary itself is a stdio MCP server. Zero dependencies — no Node, no npm. Content never leaves your machine. It needs your API key, and exposes the scanning tools (scan_payload,scan_file,scan_bundle) plus the account tools (history, usage, profiles, plans). See Local scanning below. - Hosted MCP endpoint: a remote URL you point a
url-based MCP client at. Best for inline payload scanning, profiles, account, and usage. No install. - Local MCP server (
npx github:tendrl-inc-labs/surface-mcp): a richer stdio server that runs on your machine. Adds API key management, the Surface skills, and a.mcpbbundle, and can proxy scans to the hosted API. See Local server below.
Local scanning (scanner binary)#
For local scanning, point your MCP client straight at the scanner binary — no Node/npm layer required:
{
"mcpServers": {
"surface": {
"command": "surface-scanner",
"args": ["--mcp"],
"env": { "SURFACE_API_KEY": "<token-from-the-dashboard>" }
}
}
}
This runs the binary as a native stdio MCP server exposing three tools, all scanned locally. Nothing about a scan leaves your machine: the server reports records only when it is started with --report-results.
| Tool | Description |
|---|---|
scan_payload |
Scan raw text content: messages, JSON, code snippets, tool calls (10 MB cap, same as --stdin) |
scan_file |
Scan a file by absolute path |
scan_bundle |
Scan several payloads (e.g. main.py, boot.py, config.json) in one call and get an aggregate deploy_safe go/no-go, for gating a device deployment before you flash |
The API key also unlocks the account tools and keeps threat feeds updating automatically. Add --report-results to the args if you additionally want each local scan recorded in your dashboard history, which counts it against your monthly quota:
{
"mcpServers": {
"surface": {
"command": "surface-scanner",
"args": ["--mcp"],
"env": { "SURFACE_API_KEY": "${SURFACE_API_KEY}" }
}
}
}
With the key set, tools/list additionally includes these tools, which call the hosted Surface API (only these calls go over the network — scanning stays local either way):
| Tool | Description |
|---|---|
get_scan |
Retrieve results for a deferred scan by scan ID |
get_scan_history |
Browse past scans (paginated) |
get_scan_detail |
Full result for a historical scan |
get_usage |
Check scan quota (used vs monthly limit) |
get_account |
Account details |
get_plans |
List available billing plans and scan limits |
list_profiles |
List available scan profiles and their configurations |
create_profile |
Create a new scan profile |
update_profile |
Update an existing scan profile |
delete_profile |
Delete a scan profile |
list_api_keys |
List Surface API keys (metadata only — secrets are never returned) |
If the API is unreachable, account tool calls return a clean error and the scanning tools keep working — local scans never depend on the network.
On a first-ever launch the binary downloads threat feeds in the background; the MCP handshake completes immediately and the first scan tool call waits until feeds are ready (progress is logged to stderr). Account tools don't wait — they work as soon as the server is up.
The scanner's daemon mode also serves MCP over streamable HTTP at http://localhost:8080/mcp, for url-based clients that should share one long-running local scanner.
If you also want API key create/delete tools, the Surface skills, or the .mcpb bundle, use the npx github:tendrl-inc-labs/surface-mcp server instead. Doc search (search_docs) is a hosted-endpoint tool only; the npx server publishes the docs as MCP resources under surface://docs/... rather than as a search tool.
Hosted endpoint#
POST https://app.tendrl.com/surface/mcp
Requires an API key: Authorization: Bearer <API_KEY>. The endpoint is POST-only JSON-RPC.
The hosted endpoint is being validated against url-based MCP clients. If a url client fails to connect, use the local npx github:tendrl-inc-labs/surface-mcp server instead.
Hosted tools#
| Tool | Description |
|---|---|
scan_payload |
Scan raw text content: messages, JSON, code snippets, tool calls. Detects prompt injection, SQL/XSS injection, credential leaks, malicious code |
get_scan |
Retrieve results for a deferred scan by scan ID |
get_scan_history |
Browse past scans (paginated) |
get_scan_detail |
Full result for a historical scan |
list_profiles |
List available scan profiles and their configurations |
create_profile |
Create a new scan profile |
update_profile |
Update an existing scan profile |
delete_profile |
Delete a scan profile |
get_account |
Account details |
get_usage |
Check scan quota (used vs monthly limit) |
list_api_keys |
List Surface API keys |
get_plans |
List available billing plans and scan limits |
search_docs |
Search Surface documentation and return relevant snippets |
The hosted endpoint does not scan files. To scan files via MCP, use the scanner binary's --mcp mode or the local npx github:tendrl-inc-labs/surface-mcp server — both expose a scan_file tool.
Client setup (hosted endpoint)#
These configs point a url-based client at the hosted endpoint.
Cursor#
In .cursor/mcp.json:
{
"mcpServers": {
"surface": {
"url": "https://app.tendrl.com/surface/mcp",
"headers": {
"Authorization": "Bearer <SURFACE_API_KEY>"
}
}
}
}
VS Code#
In .vscode/mcp.json:
{
"servers": {
"surface": {
"type": "http",
"url": "https://app.tendrl.com/surface/mcp",
"headers": { "Authorization": "Bearer <SURFACE_API_KEY>" }
}
}
}
Claude Desktop#
In claude_desktop_config.json:
{
"mcpServers": {
"surface": {
"url": "https://app.tendrl.com/surface/mcp",
"headers": {
"Authorization": "Bearer <SURFACE_API_KEY>"
}
}
}
}
Local server (npx)#
Surface's local stdio MCP server installs straight from its GitHub repository — there is no npm package to publish or trust. If local scanning plus the account tools covers you, the scanner binary's --mcp mode does that with no Node layer at all — reach for this package when you also want API key create/delete, the Surface skills, or a .mcpb bundle, or when a url-based client can't reach the hosted endpoint. It exposes a different toolset from the hosted endpoint — broader in most respects, but without search_docs:
- The hosted scanning, account and profile tools (except
search_docs), plus: scan_file: scan a file by absolute pathscan_bundle: scan several payloads (e.g.main.py,boot.py,config.json) in one call and get an aggregatedeploy_safego/no-go, for gating a device deployment before you flashcreate_api_key/delete_api_key: manage Surface API keys
It also serves the Surface skills as MCP resources (see AI Skills). The hosted endpoint serves documentation as resources but not skills.
Install it by repository, not by name. An unrelated third-party package
called surface-mcp exists on npm; installing that name runs someone else's
code. The github:tendrl-inc-labs/surface-mcp specifier below is
unambiguous, and npm builds it from source on install.
Add it to your MCP client config:
{
"mcpServers": {
"surface": {
"command": "npx",
"args": ["-y", "github:tendrl-inc-labs/surface-mcp"],
"env": {
"SURFACE_KEY": "${SURFACE_KEY}"
}
}
}
}
By default, scan_file, scan_payload, and scan_bundle upload to the Surface API. To scan locally (content never leaves your machine), set SURFACE_SCANNER_PATH to a local scanner binary. All three then run every scan through the local binary instead:
{
"mcpServers": {
"surface": {
"command": "npx",
"args": ["-y", "github:tendrl-inc-labs/surface-mcp"],
"env": {
"SURFACE_KEY": "${SURFACE_KEY}",
"SURFACE_SCANNER_PATH": "/path/to/scanner"
}
}
}
}
See Scanner Binary for details on local vs API mode.
Tendrl