Behnam Analytics

Writing AI workflows & prompting

MCP servers for data work

What the Model Context Protocol is, how Claude Code connects to MCP servers, three patterns for analytics teams (read-only database access, documentation lookup and ticketing), and the governance that has to come first.

Behnam Ebrahimi 8 min read

Claude Code can read your repository. An MCP server lets it reach systems outside it: a database, a documentation site, a ticket tracker. For analytics work that removes a lot of copying and pasting. It’s also where the governance questions start, because whatever a server returns goes into the conversation, and so to the model.

This article covers what the protocol is, how Claude Code connects to servers, three patterns that suit data teams, and the controls I’d put in place before any of them. It follows the Claude Code MCP documentation and the protocol’s own site, modelcontextprotocol.io, at the time of writing.

What MCP is

The Model Context Protocol is an open standard for connecting AI applications to external systems. It has three roles:

  • Host: the AI application, such as Claude Code.
  • Client: a connection the host keeps open to one server. The host creates one client per server.
  • Server: a program that provides context and capabilities. It can run locally as a process (the stdio transport) or remotely over HTTP (the Streamable HTTP transport). Messages use JSON-RPC 2.0 either way.

A server offers three kinds of thing:

Primitive What it is Who decides to use it Data example
Tools Functions the model can call The model Run a query, create an issue
Resources Read-only data provided as context The application A table schema, a document
Prompts Reusable instruction templates The user “Summarise this ticket”

The specification is direct about the risk. Tools represent arbitrary code execution, hosts must get the user’s consent before invoking one, and there should always be a person in the loop who can deny a tool call. Everything below is about keeping that true.

How Claude Code connects

Add a server from the command line. A remote server needs a transport and a URL; a local one needs the command that starts it, after a -- that separates Claude Code’s own options from the server’s:

claude mcp add --transport http example https://mcp.example.com/mcp
claude mcp add --env API_KEY=your-key --transport stdio example-local -- npx -y @example/mcp-server

Each server is saved at one of three scopes:

Scope Loads in Shared Stored in
Local (the default) This project, for you No ~/.claude.json
Project This project, for everyone Yes, through git .mcp.json at the project root
User All your projects No ~/.claude.json

When the same server is defined in more than one place, Claude Code connects once, using the whole entry from the highest-ranked source: local, then project, then user, then plugins, then claude.ai connectors. claude mcp list, claude mcp get <name> and claude mcp remove <name> manage servers from the shell, and /mcp inside a session shows each server’s status, handles sign-in, and lets you switch one off without deleting it.

A project’s .mcp.json is the one to share, and it supports ${VAR} and ${VAR:-default} expansion, so credentials stay in each person’s environment instead of in git:

{
  "mcpServers": {
    "warehouse-dev": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@bytebase/[email protected]", "--dsn", "${WAREHOUSE_DEV_RO_DSN}"]
    },
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": {
        "Authorization": "Bearer ${GITHUB_MCP_PAT}",
        "X-MCP-Toolsets": "issues,pull_requests",
        "X-MCP-Readonly": "true"
      }
    }
  }
}

In an interactive session, Claude Code asks you to approve each server from .mcp.json before using it; claude mcp reset-project-choices resets those answers.

A few behaviours matter for data work:

  • Tools get prefixed names. A tool appears as mcp__<server>__<tool>, such as mcp__warehouse-dev__execute_sql. Permission rules and hook matchers use that name.
  • Definitions load on demand. By default Claude Code starts with only tool names and server instructions and loads full definitions when Claude needs them, so adding servers costs little context.
  • Results have a size limit. Claude Code warns when a tool’s output passes 10,000 tokens and caps it at 25,000 by default (MAX_MCP_OUTPUT_TOKENS changes that). A larger result is saved to a file, which Claude reads when it needs the content.

Three patterns for data work

Read-only database access

The day-to-day value is small, frequent questions: what are the columns in this table, is this join key unique, how many rows fall in last month. The Claude Code docs use DBHub as their database example, and it’s the server in the .mcp.json above. By default it exposes two tools, execute_sql and search_objects, and its own configuration has a read-only mode and a row limit.

Treat those settings as a second layer. The control that holds whatever the model does is the database login: one that can only read, only the schemas or views the work needs, on a development copy holding synthetic or properly de-identified data. The connection string lives in an environment variable, never in .mcp.json. Then let Claude Code’s permissions separate looking from querying:

{
  "permissions": {
    "allow": ["mcp__warehouse-dev__search_objects"],
    "ask": ["mcp__warehouse-dev__execute_sql"]
  }
}

Schema searches run without a prompt; every query waits for your approval. And ask for aggregates rather than rows. A result set is context, and a count answers most questions an extract would.

Documentation lookup

Data dictionaries, agreed metric definitions and wiki pages often live outside the repository. A server that exposes them as resources lets you reference them in a prompt with @server:protocol://path, the way you’d reference a file, or search them with a tool. If the documents are already in the repo, you don’t need a server: Claude can read the files.

Documentation servers are read-only by nature, so the risk is the content. The Claude Code docs warn that servers which fetch external content can expose you to prompt injection. Prefer internal, curated sources, and treat retrieved text as data to check, not instructions to follow.

Ticketing

The ticket often holds the real specification: the acceptance criteria, the metric definition agreed with the requester, the date range. Reading it directly beats paraphrasing it into a prompt. The github entry above connects to GitHub’s remote server with a fine-grained personal access token limited to the repositories you choose. The X-MCP-Readonly header, from GitHub’s server documentation, disables every tool that writes, and X-MCP-Toolsets limits the rest to issues and pull requests.

Two details. In a remote server’s URL and headers, Claude Code reads certain credential variables as empty, including its own ANTHROPIC_API_KEY, so it never sends them to a server; give the token a variable name of your own, as above. And keep write tools off unless you need them. Posting to stakeholders is something a person should do.

Governance comes first

Least privilege at every layer

Layer Control
Credential A read-only login limited to the schemas or views needed; tokens scoped to named repositories
Server Its own read-only mode, tool selection and row limits
Claude Code Allow rules for read tools, ask rules for anything that queries or writes, "deny": ["mcp__*"] in projects that need no MCP at all
Organisation A managed allowlist: allowedMcpServers with allowManagedMcpServersOnly: true

The organisation row is worth a warning. Allowlist entries can match a server by serverUrl, serverCommand or serverName, and the docs say plainly that a name is not a security control, because users choose the label. Match on URL or command.

Never point an agent at production patient data

A read-only login stops writes. It doesn’t stop disclosure: every row a query returns goes into the conversation. Agents belong on synthetic or de-identified data, and on production data only where your information governance process has approved it; for identifiable patient data the answer should usually be no. Synthetic health data: what it’s for and where it stops covers what a synthetic copy can and can’t stand in for.

The same reasoning covers where a server runs. A local server runs with your user account’s privileges, and the MCP project’s security guidance lists arbitrary code execution and data exfiltration among the risks of a compromised or untrusted one.

Audit what a server can do

Before connecting a server:

  1. Read its tools. Ask Claude to list the server’s tools and their descriptions, then check them against the server’s source or documentation. The specification says clients must treat a server’s own descriptions of tool behaviour as untrusted unless the server is trusted.
  2. Pin the version. npx -y some-package runs whatever was published last. Pin a version, as in @bytebase/[email protected], and upgrade on purpose.
  3. Know who vouches for it. Anthropic reviews connectors against listing criteria before adding them to its Directory, but in its own words it “does not security-audit or manage any MCP server”. Its advice is to write your own or use servers from providers you trust; for a database or ticket tracker, that usually means the vendor’s own server.
  4. Trace the credentials. Where the token comes from, what it can reach, and that .mcp.json holds only ${VAR} references.

Once it’s running:

  • Log every call. A PreToolUse hook with the matcher mcp__warehouse-dev__.* can append each query to a file; Claude Code hooks for analytics repos shows the pattern. Across an organisation, OpenTelemetry export with OTEL_LOG_TOOL_DETAILS=1 records which servers and tools people use.
  • Watch scripted runs. claude -p can’t show the approval prompt, so it loads a repository’s .mcp.json servers without asking. Review a repo before scripting against it, reject servers by name with disabledMcpjsonServers, or start with --strict-mcp-config so only the servers you pass with --mcp-config load.

MCP turns an agent from something that reads your code into something that reads your systems. That’s worth having for schema lookups, definitions and tickets, as long as the credentials, not the model’s good intentions, decide what it can reach. For the rest of the setup, see A Claude Code workflow for analysts and BI developers.

Tags

  • claude-code
  • mcp
  • governance
  • security
  • databases