feat(plugins): Network Pack — HTTP + WebSocket with URL allowlist - #27
Merged
Conversation
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
force-pushed
the
feat/network-tools
branch
from
May 12, 2026 12:33
e62c638 to
49ed80d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
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
Allowlist patterns
Glob-style on full URL:
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
🤖 Generated with Claude Code