What Is an MCP Server? A Plain Guide for Product and Engineering Teams
By Sebastiaan Jansen · · 8 min read
An mcp server is a small program that exposes tools, data, and prompts to an AI client through one standard. The client calls a tool, the server does the work, and the result comes back. If you searched what is mcp, here is the short answer: the model context protocol is the contract, and the server is the side that holds your systems.
Teams run an mcp server so Claude, Codex, an IDE assistant, or a custom agent can read a repository, open a ticket, or pull an error without someone writing a private integration for every product pair.
What an mcp server is
An MCP server is a process that speaks the Model Context Protocol. It isn't a website, a chatbot, or a model. A host starts one or more clients, and each client keeps a session with one server. The server says what it can do, and the model decides when to call it.
There are three roles:
- Host: the application on screen, such as a desktop app, an IDE, or a coding agent.
- Client: the connector inside that host. One client holds one server session.
- Server: the process that wraps a real system, such as Git, a tracker, a database, a docs folder, or an error log.
Tools have names and input schemas. Resources have URIs the client can read. Prompts are reusable templates the server offers. The model still chooses the words, and the server only answers the calls it was written to answer.
You can swap the host and keep the server. The same ticket server can sit behind more than one coding agent, which is why it comes up in AI project management once agents start filing work instead of only generating diffs.
What the server does and does not do
The server carries out calls. People still make the product decisions.
| The server does | The server does not |
|---|---|
| List tools, resources, and prompts | Pick the model or the vendor |
| Validate inputs against a schema | Decide roadmap priority |
| Call your API, CLI, or database | Merge a pull request on its own |
| Return text, JSON, or an error | Hide a failed call as a success |
| Run where you deployed it | Train on your data by default |
A local server runs as a process on a laptop, often over stdio. A remote server is one the team hosts, usually over HTTP with authentication. Both follow the same protocol, but you trust them differently.
How a call moves
- The host starts the client and connects it to the server.
- The client asks what the server offers.
- The server returns tool names, JSON schemas, resource URIs, and prompts.
- A person types a request, or a ticket starts an agent run.
- The model picks a tool and fills in the arguments.
- The client sends the call. The server checks the arguments, does the work, and returns content or an error.
- The model reads the result and either calls another tool or answers.
The server holds the token. The model sees a tool name and a schema, never the API key. If a tool asks the model for a raw token, the design is wrong.
Reads are easy. Writes need a version (the etag, hash, or updated_at from the last read), and the server should refuse the write if the record has changed since. Without that check, two agents, or an agent and a human, will overwrite each other. The protocol leaves this part to you.
What teams expose on a server
Keep the server narrow. Ten tools for one job are better than "everything in the company."
- Repository: list files, read a file, search symbols, open a diff. The agent plans against the code that actually exists, which is the point of Claude Code for teams and of OpenAI Codex in a shared workflow.
- Work tracker: create a ticket, move a stage, comment, list the current cycle.
- Errors: pull an issue, the stack, the release, and whether it is still firing.
- Docs: read one runbook or decision record by URI instead of dumping a whole drive into the prompt.
- Prompts: a server-owned template such as "write the test plan for this ticket."
A resource is something you read, like repo://src/billing/total.ts. A tool is something you invoke, like create_ticket. Put context in resources and state changes in tools. A plugin lives inside one host, while a server attaches to any host that speaks the protocol.
A worked example
Sentry fires InvoiceTotalMismatch on POST /invoices: 40 events in two hours, release 2026.10.03. These counts are only an illustration.
Three servers are connected. The errors server has get_issue and list_events, both read only. The repo server has search and read_file, also read only. The board server has find_ticket, create_ticket, and comment, and its writes require a version token.
The on-call engineer says: "If this is not already a ticket, file one as Bug in Triage, and name the file that computes the total."
get_issuereturns the title, the culprit framesrc/billing/total.ts:88, the release, and a count. The agent never sees the Sentry token.find_ticketwith that issue id finds no match.read_filereturns the function and the commit hash it read.create_ticketwrites type Bug, stage Triage, the stack summary, the path, the release, and the Sentry link. The server returnsBILL-241and version1.- The agent stops, because nobody asked it to change code.
A human then moves BILL-241 to Ready. The run could file a ticket but had no way to push. The connected servers work as the permission list, which is the control point in using coding agents without losing the board. If git push is not a tool, the model cannot push.
Everything in the ticket came from a server result: title, file and line, release, a duplicate check, and an id a person can open. If a call times out, the ticket says it timed out. The agent doesn't make up a stack.
Falrow is one product built this way. Slack threads, call notes, and Sentry errors become tickets, and Claude Code or Codex plan from the git repository through Falrow's MCP server, with version-checked writes under stage rules. The protocol doesn't require Falrow, though. Compare hosts by their tool lists, and a published MCP tool catalogue is a reasonable place to start.
What belongs on the server
Add a tool when someone already does that job every week, the inputs fit a schema, you can name the role allowed to call it, and a wrong call is either reversible or impossible because the tool is read-only. Leave out mail, deploys, payments, and deletes unless a second person has to approve them. Paginate large results, and don't add near-duplicate tools.
Names should be boring and stable: create_ticket beats make_it_so. Renaming a tool breaks every saved prompt that called it, so treat tool names the way you treat API paths. Give each role its own credentials instead of sharing one admin token.
| Role | Typical tools | Writes |
|---|---|---|
| PM / triage | list, create, comment | yes, no stage skip |
| Engineer | read repo, search, read ticket | branch only if you allow it |
| On-call | read errors, file bug | create only |
| CI agent | read repo, post a check | no ticket create |
Sooner or later a hostile README or ticket body will try to talk the model into a bad call. Schema checks won't stop that. A missing tool will, and so will a server-side rule that the agent's identity can't override.
Safety checks before you connect one
Someone on the team will paste a community server command because a README told them to. That command runs as whoever launched it.
- Read what it executes. A local server is code running with that account's rights.
- Pin the version. With a floating install, one bad publish becomes a supply-chain break.
- Prefer servers you can vendor or review, and keep your own tracker and repo servers small.
- Authenticate remote servers. A public endpoint with no auth is a tool anyone can call.
- Log the tool name, redacted arguments, caller, and status.
- Keep read tokens separate from write tokens, and stay read-only until you have read a week of logs.
Codex and GitHub, from issue to pull request uses the same guardrail: the agent may draft the change, and a person or a protected branch lands it. Drafting should be easy, and landing should be hard.
Where it sits in the workflow
The server doesn't replace the board, the repo, or review. It's an adapter in front of them.
The tracker stays the source of truth for stage, owner, and cycle, and the repo stays the source of truth for code. The agent proposes, and a person or a written rule accepts. Cycle time still comes from tickets, and every agent edit carries an actor, a timestamp, and a version.
Start read-only with a repo server and a ticket server: understand the code, file or update the work item, then stop. Add an errors server once on-call gets tired of pasting stacks. Falrow's agent surface runs that loop under workflow rules, against tickets that already have types and stages. Two small servers on your own host work the same way, as long as every write names a role, carries a version, and stops at a clear point.
FAQ
Is an MCP server the same thing as an API?
No. Your API still does the real work. The server is a thin, discoverable layer over a few operations, so an AI client can list them and call them with a schema. Most servers call an existing API or CLI underneath. Keep the API for product traffic, and add a server only for the calls an agent should make.
Can one server talk to several agents?
Yes. Each host runs its own client session. Give each host its own credentials and tool allow-list, because a shared server doesn't mean shared permissions. That's the practical difference from a plugin that exists in only one IDE.
Do we have to run it in the cloud?
No. Local stdio is the default for one repo on a laptop. Use a remote server for shared team state, central logs, or a host that can't launch processes. Start local and read-only.
What should we build first?
Build the read path you already trust: repository search and file read, or list-tickets and get-ticket. Spend a week checking that the agent cites real files and real ids. Then add one write, create_ticket or comment, with a version check and a role that can't skip stages. Keep deploys, payments, and deletes off the server until nobody can skip the human approval step.