Skip to content

feat(plugins): Network Pack — HTTP + WebSocket with URL allowlist - #27

Merged
rrader26 merged 1 commit into
mainfrom
feat/network-tools
May 12, 2026
Merged

feat(plugins): Network Pack — HTTP + WebSocket with URL allowlist#27
rrader26 merged 1 commit into
mainfrom
feat/network-tools

Conversation

@rrader26

Copy link
Copy Markdown
Contributor

Summary

  • New `createNetworkPlugin()` adds 5 outbound network tools (HTTP + WebSocket).
  • Bounded by a glob-style URL allowlist that is empty by default — refuses every call until the operator explicitly configures one.
  • Pure Node, no external dependencies (native global `WebSocket` from Node 22+).

Why

Closes a real gap: today an agent can drive a running app, drive a browser, or hit Microsoft Graph, but has no way to call an arbitrary JSON API or stay connected to a realtime stream. Lots of agent workflows boil down to "look up a record in our system, then..." — that needs an HTTP call.

The allowlist is non-negotiable. Default deny prevents the AI from being a data-exfil vector to attacker-controlled endpoints.

Tools

Tool Use
`agentmark_http_request` Any HTTP method; auto-encoded JSON/string/base64 body; text/json/base64 response
`agentmark_websocket_connect` Open WS; returns `ws_id`
`agentmark_websocket_send` Text or base64 binary
`agentmark_websocket_receive` Pull queued OR wait for next message with timeout
`agentmark_websocket_close` Send close frame + release

Allowlist patterns

Glob-style on full URL:

  • `*` matches any character except `/`
  • `**` matches any character including `/`
  • `https://api.example.com/**\` — any path under that host
  • `https://*.example.com/**` — any subdomain
  • `wss://*.realtime.example.com/**` — WebSocket allowlist

Patterns come from `createNetworkPlugin({ urlAllowlist })` and `AGENTMARK_HTTP_ALLOWLIST` (colon-separated), merged.

Usage

```ts
const network = createNetworkPlugin({
urlAllowlist: [
'https://api.github.com/',
'https://*.internal.example.com/
',
'wss://stream.example.com/**',
],
})
createMcpServer({ plugins: [web, pdf, desktop, network, meta] })
```

Test plan

  • `pnpm build` clean
  • `pnpm test` — 359 pass / 10 skip (17 new tests via mocked `fetch`: allowlist matching, HTTP body encoding, JSON/base64 response paths, refusal paths, WS allowlist boundary)
  • Manual: full WS round-trip against a real echo server (integration suite — separate)

🤖 Generated with Claude Code

Cross-platform pure-Node plugin for outbound network calls. Bounded by
a glob-style URL allowlist (empty by default → denies everything until
the operator configures one) so the agent can't exfiltrate to arbitrary
endpoints.

Tools shipped (5):
  agentmark_http_request          GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS
  agentmark_websocket_connect     open WS, returns ws_id
  agentmark_websocket_send        text or base64 binary
  agentmark_websocket_receive     pull queued or wait for next message
  agentmark_websocket_close       close + release session

HTTP body encoding is auto-detected:
  - JSON object → stringified, content-type defaults to application/json
  - plain string → sent verbatim
  - { base64: "..." } → decoded to raw bytes (octet-stream)

Response body shape selectable via `response_format`:
  - "text" (default)
  - "json" (parsed; errors clearly if not valid JSON)
  - "base64" (for binary content like images / PDFs)

WebSocket sessions queue inbound messages; receive() returns up to
`max` messages, or waits up to `timeout_ms` for the first one if the
queue is empty. Returns empty array on timeout or after socket closes.

Uses native global `WebSocket` (Node 22+, browsers). No external deps.

Allowlist matching is glob-style:
  - "*"  matches non-slash run
  - "**" matches any character including slash
  - "https://api.example.com/**"        — any path under that host
  - "https://*.example.com/**"          — any subdomain
  - "wss://*.realtime.example.com/**"   — WebSocket

Opt-in plugin. Configure via createNetworkPlugin({ urlAllowlist: [...] })
or AGENTMARK_HTTP_ALLOWLIST (colon-separated).

Tests (17 new, 359 total): allowlist semantics, single/double-star
matching, wildcard subdomains, HTTP method shaping, JSON body encoding,
base64 response decoding, boundary refusal paths, websocket allowlist
enforcement. Build clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rrader26
rrader26 force-pushed the feat/network-tools branch from e62c638 to 49ed80d Compare May 12, 2026 12:33
@rrader26
rrader26 merged commit 17c3e71 into main May 12, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants