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.
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 asmcp__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_TOKENSchanges 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:
- 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.
- Pin the version.
npx -y some-packageruns whatever was published last. Pin a version, as in@bytebase/[email protected], and upgrade on purpose. - 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.
- Trace the credentials. Where the token comes from, what it can reach, and that
.mcp.jsonholds only${VAR}references.
Once it’s running:
- Log every call. A
PreToolUsehook with the matchermcp__warehouse-dev__.*can append each query to a file; Claude Code hooks for analytics repos shows the pattern. Across an organisation, OpenTelemetry export withOTEL_LOG_TOOL_DETAILS=1records which servers and tools people use. - Watch scripted runs.
claude -pcan’t show the approval prompt, so it loads a repository’s.mcp.jsonservers without asking. Review a repo before scripting against it, reject servers by name withdisabledMcpjsonServers, or start with--strict-mcp-configso only the servers you pass with--mcp-configload.
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