Logflare MCP Code Mode

Logflare MCP Code Mode

Enables agents to interact with the Logflare API by writing JavaScript functions that run in a Vercel Sandbox, using three tools: search, execute_read, and execute_write.

Category
访问服务器

README

Logflare MCP Code Mode

A Model Context Protocol server that gives an agent three tools instead of dozens for driving the Logflare Management API. The agent writes a short JavaScript function that runs inside a Vercel Sandbox and calls the API through a thin api.request() client — looping, filtering, and chaining calls in one round-trip.

This applies Cloudflare's Code Mode pattern to Logflare:

  • LLMs write API-calling code more reliably than long chains of individual tool calls.
  • Intermediate data stays in the sandbox, out of the model's context.
  • The agent discovers endpoints with search (the OpenAPI spec), then calls them with execute_read/execute_write.

This is a local stdio server — one process per MCP client, communicating over stdin/stdout, the same way you'd run any locally-installed MCP server (npx, or a command/args entry in your client config). Logflare has no OAuth 2.1 authorization server, so there's no per-request bearer token to broker the way a remote streamable-HTTP server would; the API key is just an environment variable for the whole process.


Quickstart

Prerequisites: Node.js ≥ 20.12, pnpm (corepack enable provides it), a Vercel account with Sandbox access, and a Logflare API key (Logflare account → API Keys).

pnpm install
pnpm build

Copy .env.example to .env and fill in VERCEL_TOKEN/VERCEL_TEAM_ID/VERCEL_PROJECT_ID and LOGFLARE_API_KEY — the local .env is only for pnpm dev/pnpm test; a real MCP client sets these as env entries in its own config instead (see below).

Connect an MCP client — this repo ships a .mcp.json:

{
  "mcpServers": {
    "logflare-code-mode": {
      "command": "node",
      "args": ["dist/index.js"],
      "env": {
        "LOGFLARE_API_KEY": "${LOGFLARE_API_KEY}",
        "VERCEL_TOKEN": "${VERCEL_TOKEN}",
        "VERCEL_TEAM_ID": "${VERCEL_TEAM_ID}",
        "VERCEL_PROJECT_ID": "${VERCEL_PROJECT_ID}"
      }
    }
  }
}

Once published, the same thing works via npx:

{
  "mcpServers": {
    "logflare-code-mode": {
      "command": "npx",
      "args": ["-y", "logflare-mcp-code-mode", "--read-only"],
      "env": { "LOGFLARE_API_KEY": "...", "VERCEL_TOKEN": "...", "VERCEL_TEAM_ID": "...", "VERCEL_PROJECT_ID": "..." }
    }
  }
}

Run node dist/index.js --help for the full option/env-var reference.

Try it — call execute_read with an async arrow function:

async () => {
  const res = await api.request({ method: 'GET', path: '/api/sources' })
  return { status: res.status, count: res.data.length }
}

You get back e.g. { "status": 200, "count": 3 } — produced by code the agent wrote, run in a sandbox that can reach only the Logflare API.


The three tools

Each takes a single code string — an async arrow function that returns a value. console.log output comes back separately as stdout.

Tool What it does In scope Annotation
search Query the Logflare OpenAPI spec (all $refs resolved) to find endpoints and their shapes. Network-isolated. spec read-only
execute_read Call the Logflare API via api.request()GET only. api read-only
execute_write Call the Logflare API via api.request() — any method (POST/PUT/PATCH/DELETE). api destructive

The workflow is: search to find an endpoint's path and request shape, then execute_read/execute_write to call it. The client is api.request({ method, path, query?, body?, contentType?, rawBody? }), which returns { status, ok, data }.

Run with --read-only to lock the whole server instance into read-only mode: search's spec only contains GET operations (so the agent can't even discover write endpoints), and execute_write isn't registered at all — it's absent from tools/list, and calling it anyway fails as an unknown tool. This is a startup flag (a property of the process), not a per-call one.


How it works

MCP client ──stdio (newline-delimited JSON-RPC)──▶ McpServer (2 or 3 tools)
                                                          │
                          ┌───────────────────────────────┼───────────────────────────────┐
                        search                       execute_read                   execute_write
                          │                                └───────────────┬───────────────┘
                          ▼                                                ▼
                 fresh Vercel Sandbox                             fresh Vercel Sandbox
                 (networkPolicy: deny-all)                        (networkPolicy: allow logflare.app;
                 embeds `spec`                                     LOGFLARE_API_KEY injected;
                                                                    `api.request()` client)
  • TransportStdioServerTransport from the MCP SDK: reads newline-delimited JSON-RPC from stdin, writes responses to stdout. stdout is exclusively the protocol channel — all logging goes to stderr (see src/log.ts).
  • Per call — a fresh Vercel Sandbox (@vercel/sandbox), stopped in finally. No SDK is installed inside it, so there's nothing to amortize with a warm pool.
  • search sandboxnetworkPolicy: "deny-all"; the slimmed OpenAPI spec (fetched from LOGFLARE_OPENAPI_URL, default https://api.logflare.app/api/openapi) is embedded as spec for the agent's read-only code to query. In --read-only mode the spec is filtered to GET-only operations first.
  • execute_* sandboxnetworkPolicy: { allow: ["logflare.app"] } (the Management API's actual host); LOGFLARE_API_KEY is injected as an env var and sent as Authorization: Bearer <key> by api.request(). The read/write split and the credential/backend/team block are enforced by the provided api.request() client: execute_read throws on any non-GET before the fetch, and both tools refuse the paths in BLOCKED_PATH. This is a guard against accidental or confused agent behavior, not a hard sandbox capability restriction — agent code is plain Node with the global fetch in scope, so it could in principle call logflare.app directly and bypass both checks. Safety for a genuinely adversarial caller rests on the fact that they're using their own API key against an API they already have full access to, not on this guard.
  • Runner — agent code is written to a file and run with node (never eval); output returns via a result file. The code sits on its own lines in the invoke scaffold, so a trailing // comment can't swallow the closing tokens.
  • Vercel credentials — explicit VERCEL_TOKEN/VERCEL_TEAM_ID/VERCEL_PROJECT_ID are required (this process never runs on Vercel itself, so there's no ambient OIDC fallback in practice).

The source is small and layered by responsibility:

File Responsibility
src/index.ts Entry point: parses --read-only/--help, loads .env, starts the stdio transport.
src/server.ts The MCP surface: the two or three tools (depending on read-only mode), response formatting, the untrusted-data boundary.
src/sandbox.ts Sandbox plumbing: runner construction, the two sandbox shapes, the injected api.request() client, output caps, the credential/backend guard.
src/spec.ts Fetches, $ref-resolves, slims, caches, and (optionally) GET-filters the Logflare OpenAPI spec for search.
src/log.ts pino logger (stderr only) + credential redaction.

Security notes

Agent code is untrusted but runs inside a Vercel Sandbox microVM and authenticates with the caller's own Logflare key — so by design it can use that key against the Logflare API. Beyond the sandbox isolation, per-tool egress, and read/write split above:

  • Untrusted-data boundary — all execute output (result/stdout/stderr) may embed API content (log events, source metadata), so it's wrapped in a <untrusted-data-{uuid}>…</> envelope (error path too), with the model told not to follow instructions inside. Truncation happens before wrapping, so a size cap can't sever the closing tag.
  • Credential/backend/team block — paths containing access-tokens (mints/lists/reveals full API tokens), backends (backend config can embed DB passwords, service-account keys, and other connection secrets), or teams (the Team response embeds the full User object, whose api_key is the account's master Logflare API key) are refused, matched anywhere in the path (e.g. /api/sources/{id}/backends/{id}). The path is first normalized to a fixpoint (percent-decode, re-resolve dot-segments, collapse slashes, lowercase), so encoded spellings (/%61ccess-tokens, //backends, /x%2F..%2Fbackends, /TEAMS) still match; it only over-blocks. This guard covers the intended api.request() path; see the caveat above about raw fetch in the sandbox.
  • Known gap — some Source fields (slack_hook_url, webhook_notification_url) embed secrets the caller configured (their own Slack webhook, etc.) rather than Logflare-minted credentials. These are not blocked, the same way reading your own resource's other fields isn't blocked — but a prompt-injected agent could still surface them into its context via execute_read/execute_write, same as any other resource field.
  • Output caps — bounded inside the sandbox before reaching the server: runner result cap (400k chars), head -c reads (500k file / 20k per stream), final server truncate (100k result / 10k logs).
  • Token location — like Vercel Sandbox generally, there's no outbound-fetch proxy hook to keep the token out of the isolate, so LOGFLARE_API_KEY lives in the sandbox env (agent code can read it). Safety rests on isolation + egress — the key only reaches logflare.app — not token secrecy.
  • Single-tenant by design — there's no bearer-token auth gate because there's nothing to gate: this process is spawned per-caller by an MCP client, already scoped to whoever's shell/config started it, unlike the remote-HTTP shape this was originally built as (see git log — the streamable-HTTP version is preserved in history if a remote deployment is ever needed again).

Configuration

Env var / flag Default Meaning
LOGFLARE_API_KEY (env) Required. The Logflare API key used for every execute_read/execute_write call.
VERCEL_TOKEN (env) Required. Vercel API token for sandbox creation.
VERCEL_TEAM_ID (env) Required. Vercel team ID for sandbox creation.
VERCEL_PROJECT_ID (env) Required. Vercel project ID for sandbox creation.
--read-only (CLI flag) off Hide execute_write; restrict search's spec to GET operations.
LOGFLARE_SANDBOX_RUNTIME (env) node24 Vercel Sandbox runtime the sandboxes boot from
LOGFLARE_EXEC_TIMEOUT_MS (env) 120000 Per-call budget for the agent's code
LOGFLARE_OPENAPI_URL (env) https://api.logflare.app/api/openapi Override the OpenAPI spec URL the search tool loads
LOG_LEVEL (env) info trace/debug/info/warn/error/fatal/silent
LOG_FORMAT (env) json json (structured) or pretty (dev)

Observability

Structured logs via pino to stderr only — stdout is reserved for JSON-RPC. One line per tool invocation/completion (tool name, code length, duration, calledEndpoints count for execute calls) plus a startup line noting read-only mode. Use LOG_FORMAT=pretty locally. Credentials are never logged (see redactToken in src/log.ts).


Testing

pnpm test
  • test/server.test.ts — the MCP surface via an in-memory client/server pair (tool list, annotations, error shapes, readOnly: true hiding execute_write). No key.
  • test/stdio.test.ts — end-to-end over the real stdio transport: spawns the built binary as a child process and speaks newline-delimited JSON-RPC over its stdin/stdout, exactly like a real MCP client. Covers --help, --read-only over the wire, and that stdout only ever carries JSON-RPC (logs land on stderr). No key. Requires pnpm build first.
  • test/sandbox.test.ts — offline unit tests for the credential/backend/team guard (path normalization + block regex). No key, no sandbox.
  • test/spec.test.ts — offline unit tests for the read-only spec filter (GET-only operations, paths with no GET dropped entirely). No key, no network.
  • test/live.test.ts — end-to-end against real Logflare + Vercel Sandbox: spec search, execute_read GET plus non-GET rejection, trailing-comment tolerance, error/SyntaxError surfacing inside the boundary, the credential/backend/team guard (including encoded bypasses), an execute_write create+delete round-trip, egress denial, and the stdout size cap. Runs only when LOGFLARE_API_KEY and the VERCEL_* credentials are set.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选