Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 14 additions & 25 deletions .github/workflows/node9-review-fixer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,34 +41,23 @@ jobs:
git config user.email "bot@node9.ai"
git fetch origin ${{ github.base_ref || 'main' }}

- uses: actions/setup-python@v5
with:
python-version: '3.11'

- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'

- name: Install dependencies
run: |
pip install -r .github/node9/requirements.txt
npm ci --prefer-offline
- name: Install project dependencies
run: npm ci --prefer-offline

- name: Run node9 review
env:
PYTHONUNBUFFERED: '1'
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
NODE9_API_KEY: ${{ secrets.NODE9_API_KEY }}
NODE9_API_URL: ${{ secrets.NODE9_API_URL || vars.NODE9_API_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_HEAD_REF: ${{ github.event.client_payload.head_ref || github.head_ref || github.ref_name }}
GITHUB_BASE_REF: ${{ github.event.client_payload.base_ref || github.base_ref || 'main' }}
GITHUB_REPOSITORY: ${{ github.repository }}
CI: 'true'
NODE9_TESTING: '1'
NODE9_TEST_CMD: 'npm run build 2>&1 && npm test 2>&1'
ITERATION: ${{ github.event.client_payload.iteration || '1' }}
FEEDBACK: ${{ github.event.client_payload.feedback || '' }}
DRAFT_PR_NUMBER: ${{ github.event.client_payload.draft_pr_number || '' }}
run: python .github/node9/agent.py
- uses: node9-ai/node9-pr-agent@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
node9_api_key: ${{ secrets.NODE9_API_KEY }}
node9_api_url: ${{ secrets.NODE9_API_URL || vars.NODE9_API_URL }}
github_token: ${{ secrets.GITHUB_TOKEN }}
test_cmd: 'npm run build 2>&1 && npm test 2>&1'
head_ref: ${{ github.event.client_payload.head_ref || github.head_ref || github.ref_name }}
base_ref: ${{ github.event.client_payload.base_ref || github.base_ref || 'main' }}
iteration: ${{ github.event.client_payload.iteration || '1' }}
feedback: ${{ github.event.client_payload.feedback || '' }}
draft_pr_number: ${{ github.event.client_payload.draft_pr_number || '' }}
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
### The "Sudo" Command for AI Agents.

[![NPM Version](https://img.shields.io/npm/v/@node9/proxy.svg)](https://www.npmjs.com/package/@node9/proxy)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Open in HF Spaces](https://huggingface.co/datasets/huggingface/badges/resolve/main/open-in-hf-spaces-sm.svg)](https://huggingface.co/spaces/Node9ai/node9-security-demo)
[![Documentation](https://img.shields.io/badge/docs-node9.ai%2Fdocs-blue)](https://node9.ai/docs)

Expand Down Expand Up @@ -84,6 +84,24 @@ Wrap any MCP server transparently. The AI sees the same server — Node9 interce

Or use `node9 setup` — it wraps existing MCP servers automatically.

### MCP Tool Pinning — rug pull defense

MCP servers can change their tool definitions between sessions. A compromised or malicious server could silently add, remove, or modify tools after initial trust — a **rug pull** attack.

Node9 defends against this by **pinning** tool definitions on first use:

1. **First connection** — the gateway records a SHA-256 hash of all tool definitions
2. **Subsequent connections** — the hash is compared; if tools changed, the session is **quarantined** and all tool calls are blocked until a human reviews and approves the change
3. **Corrupt pin state** — fails closed (blocks), never silently re-trusts

```bash
node9 mcp pin list # show all pinned servers and hashes
node9 mcp pin update <serverKey> # remove pin, re-pin on next connection
node9 mcp pin reset # clear all pins (re-pin on next connection)
```

This is automatic — no configuration needed. The gateway pins on first `tools/list` and enforces on every subsequent session.

---

## Python SDK — govern any Python agent
Expand Down
Loading
Loading