AI Engineer World's Fair 2025
Remote MCPs: What we learned from shipping — John Welsh, Anthropic
Read the talk
Remote MCPs: Standardize the Messages, Centralize the Shared Work
Anthropic’s internal MCP gateway separates model-context messages from transport, giving services a common path for integrations, credentials, and policy enforcement.
From a talk by John Welsh
Before you start: Familiarity with client/server APIs, JSON-RPC, and OAuth bearer tokens will help you follow the gateway and transport examples.
When every tool integration has its own interface
What happens when connecting a model to tools becomes easy, but sharing those integrations across services remains hard? John Welsh approaches that problem from twenty years of building large-scale systems and, at Anthropic, recent work on tool calling and MCP integrations. He places the turning point around the middle to end of the preceding year: models became good enough at calling tools to connect Google Drive, consult Maps, and send someone a text message in one workflow.
Teams moved quickly. Custom endpoints such as /calltool and /getcontext appeared for individual use cases, then acquired authentication requirements. The organization accumulated duplicated functionality behind incompatible interfaces. In Welsh’s illustrative example, an integration works well in service A, but using it in service B requires a three-week interface rewrite. The exciting part—giving models useful tools—has produced an ordinary distributed-systems problem: everyone has built slightly different plumbing.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Separate the message contract from the transport
At Anthropic, those homegrown interfaces began converging on the same operations: get tools, get resources, ask for additional details. They increasingly resembled Model Context Protocol (MCP), even when they did not implement its full feature set. That convergence suggests adopting the shared contract before independently rebuilding more of it.
Welsh separates MCP into two concerns:
| Concern | What it standardizes |
|---|---|
| Message contract | JSON-RPC exchanges between context providers and model-facing code |
| Transport and connection machinery | Streamable HTTP, authorization, and session management |
He locates most of the engineering value in the message semantics and server interactions. The March 2025 transport specification provides the historical context for this distinction. Its accompanying authorization specification described OAuth 2.1, then an IETF draft, as an optional authorization framework for HTTP transports—not a requirement that every MCP transport use OAuth.
Standardize how context is exchanged without requiring every internal connection to travel the same way. Welsh’s proposal to use MCP for everything is scoped to presenting context to models, not all application communication. A provider might run in the same process, in another data center, or behind enterprise networking. The consuming code should still connect to MCP and receive the same tools and methods.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Why use an existing standard internally?
Making Google Drive talk to an application is necessary work, but usually not a competitive differentiator. A single integration approach gives engineers less to learn and lets improvements made for one integration benefit the next. The goal is to spend less effort moving context between systems and more effort on the product consuming it.
MCP adds an ecosystem argument to that internal consistency argument: if customers and external services already require MCP support, maintaining a second internal contract means doing the work twice. Welsh cites participation by all major AI labs as a reason to expect new model capabilities to find their way into the protocol. That is his rationale for betting on its evolution; the immediate benefit is having protocol features available when the organization encounters the problems they address.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Let the consuming product fulfill sampling requests
Consider Welsh’s hypothetical company with four products and four billing models. Each product may also have its own token limits and usage tracking. A shared slides integration needs model generation, but embedding every product’s accounting rules inside that integration would couple it to all four products.
Sampling reverses who requests model execution. The integration sends a sampling request over the MCP stream, and the client at the other end fulfills it. This lets the slides integration request generation while the consuming product supplies the execution and accounting context. The sampling specification defines sampling/createMessage, requires the client to declare sampling support, and leaves model selection and permissions with the client, with recommended approval controls. Billing and usage accounting remain application responsibilities; MCP supplies the request boundary through which that work can be delegated.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Make the shared gateway the easiest path
Two pressures converged at Anthropic. External remote MCP services, including the historical mcp.asana.com example, required network connectivity and authentication. Internally, engineers were creating PR review bots, Slack management tools, and other experimental LLM services. Giving every service user credentials and unrestricted outbound connectivity would spread sensitive responsibilities across the organization and make auditing harder.
The response was an MCP gateway: shared infrastructure with one entry point and a connection helper that returns an MCP SDK client session. Welsh calls this a pit of success. If the supported approach is also the easiest approach, engineers naturally use it.
The gateway combines four responsibilities:
- Connection entry point: One client call opens an MCP session.
- URL-based routing: Internal and external servers use the same connection path from the application’s perspective.
- Credential management: Applications do not each implement OAuth.
- Shared operations: Rate limiting and observability have a central home.
These capabilities make the gateway useful to application engineers while concentrating the work the organization needs to control.
The architecture diagram adds a gateway between the callers and their integrations. Welsh jokes that “one more box will solve all our problems.” The concrete value of that box is the shared responsibility it removes from each caller.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Return the SDK session, not another proprietary interface
The simplified client call accepts a server URL, organization ID, and account ID. Welsh explains that the real implementation authenticates with a signed token because the gateway accesses stored credentials. Crucially, it returns an MCP SDK object directly. Applications can use the protocol’s interface rather than learning a gateway-specific replacement.
That choice also reduces the amount of wrapper code that must change as MCP evolves. Welsh describes adopting new features by updating internal MCP packages. The boundary still follows MCP’s lifecycle requirements: compatible protocol versions and negotiated capabilities determine what peers can actually use. A package update does not create support on the other end of a connection.
The same application code connects to internal and external integrations. External connections use the standardized transport; inside Anthropic’s network, the team chose WebSockets. The adapter opens a WebSocket and carries JSON-RPC messages in both directions. Internal transport selection follows the organization’s infrastructure needs while preserving the message contract.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Different transports, the same read and write streams
The adapter’s output is a read stream and a write stream. Pass those into the MCP SDK client session, and the session handles the protocol above that boundary. In Python, the essential session wiring looks like this:
python
from mcp import ClientSession
async def discover_tools(read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
return await session.list_tools()
The transport adapter supplies SDK-compatible streams; application code works with the initialized session. Welsh also suggests gRPC when multiplexing would avoid opening a separate WebSocket for each connection.
Once that boundary is clear, the transport alternatives are easy to compare:
| Transport | Role in the talk |
|---|---|
| WebSocket | Anthropic’s selected internal transport |
| gRPC | Suggested option for multiplexing connections |
| Unix socket | Another way to carry local messages |
| Email over IMAP | Humorous demonstration of transport independence |
Each adapter ultimately supplies the same read/write stream pair. These are custom transports, not additional standard MCP transports; they must still preserve MCP’s message and lifecycle requirements.
The email demonstration pushes the separation to an intentionally absurd conclusion. Messages begin with “Dear server, I hope this finds you well” and contain an “MCP request start” marker. After exchanging the messages by email, the implementation still feeds streams into a client session. IMAP is the joke; the stable SDK boundary is the mechanism.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Own OAuth once, reuse credentials across products
With transport hidden behind the session interface, the gateway also owns external OAuth complexity. It exposes helpers to obtain an authorization URL and complete the OAuth flow. Those operations accommodate different redirect destinations: Anthropic’s API and claude.ai may need users returned to different places, even though the gateway manages the credentials for both.
Central ownership makes credentials portable across consuming services. A batch job can connect using the same internal user identity without asking the user to authenticate again. The job does not need its own copy of the OAuth implementation, and internal servers do not need to manage the external tokens. Credential reuse happens through the gateway.
Welsh traces the authenticated connection through that boundary:
- Authenticate the internal connection. The service opens a WebSocket to the gateway with an authentication token in the headers.
- Retrieve external credentials. The gateway looks up the user’s stored credentials.
- Open the external connection. The gateway creates an authenticated SDK client, supplying the external bearer token in the authorization header.
- Bridge the streams. The external client’s read and write streams connect to the internal transport.
The internal authentication token establishes who is calling the gateway; the retrieved external credential authenticates the gateway’s connection to the integration.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Use the shared context path for policy
The gateway creates another useful property: a central view of the context models request and the context returned to them. Welsh raises prompt injection and the possibility of a model with Google Drive access deleting its contents as reasons to care about that visibility. The deletion example is a risk scenario, not an incident he reports observing.
That shared path offers a place to ban malicious servers, classify request content, and audit activity. Because messages have standardized shapes, inspection can target specific protocol operations: tool execution, tool definitions, or resource management. The gateway provides an enforcement point; the policies and processors still have to be built. Centralizing traffic makes those controls easier to apply consistently, but does not itself resolve prompt injection.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Centralize at the layer that removes repeated work
For a new service, adopting the shared path becomes a package import. Anthropic maintains client packages for multiple internal languages, so the architecture does not depend on every team choosing the same language. Engineers gain a familiar integration interface; operators gain a single point of ingress and egress with standardized messages. Returning SDK sessions also lets protocol evolution flow through the shared packages instead of requiring a new proprietary interface for each feature.
Welsh’s closing description of MCP as JSON streams emphasizes how little application-specific plumbing should sit between the transport and the client SDK. His broader recommendation is to standardize on something, even if the organization chooses a protocol other than MCP. Then make that standard the easiest path to use. Centralizing authentication and external connectivity at the appropriate layer removes repeated work from every service and leaves teams more time for the behavior that makes their products valuable.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
From the talk
Defines standard transports and the requirements for carrying MCP messages over a custom transport.
Explains how servers request model generations through clients, including capability declarations and approval controls.
Documents the HTTP authorization framework available around the time of the talk.
Further reading
- MCP initialization and capability negotiationDocumentation
Explains how clients and servers establish compatible protocol versions and supported capabilities.
The May 2025 announcement introducing remote MCP integrations for Claude.
Introduces the API's remote MCP connector alongside other agent-building capabilities.
Original research demonstrating how malicious tool descriptions can redirect an assistant and expose sensitive data.
Public Python SDK for implementing MCP clients and servers.
Updates since the talk
Current Asana V2 connection instructions covering OAuth app registration and authorization.
Read the complete timestamped transcript
- 0:00
[upbeat music] [audience applauding] Awesome.
- 0:17
Thanks so much for coming. Um, I wanted to give a bit of a talk on implementing MCP clients and talking remote MCP at scale within a large organization like Anthropic.
- 0:28
Um, I wanted to give first a little introduction from me. Uh, my name's John. I've spent twenty years building large-scale systems and dealing with the problems that that causes, and so I've made a lot of mistakes, and, uh, I'm excited to give maybe some thoughts on avoiding some of those mistakes.
- 0:45
I'm currently a member of technical staff here at Anthropic, and I've spent the past few months, um, focusing on tool calling and integration and implementing MCP support for all of our internal, like, external integrations within the org.
- 0:59
And so, looking at tool integration with models, we've kind of hit this timeline where, um, models only really got good at calling tools, uh, like, kind of late mid last year.
- 1:13
And suddenly everyone got very excited because, like, your model could go and call your Google Drive, and then it could call your Maps, and then it could send a text message to people.
- 1:22
And so, there's this huge explosion with, like, very little effort you can make very cool things. And so, um, teams are all trying to move fast. Everyone's moving very fast in AI.
- 1:31
Custom endpoints start proliferating for every use case. There's a lot of, like, services popping up with, like, /calltool and /getcontext, and then people, um, start to realize there's additional needs of some authentication.
- 1:44
There's been a bunch of stuff there, and this kind of led to some integration chaos where you're duplicating a bunch of functionality around your org. Nothing really works the same.
- 1:55
You have an integration that works really well in service A, but then you want to use it in service B, but it- you can't 'cause it's gonna take you three weeks to rewrite it to talk to the new interface.
- 2:04
And so we're in this kind of spot, and the place that we came to at Anthropic is realizing that over time, all of these endpoints started to look a lot like MCP.
- 2:15
Uh, you, you end up with some get tools, some get resources, some elicitation of, of details. Um,
- 2:24
and even if you're not using the entire feature space of MCP, uh, as a whole immediately, like, you're probably gonna go extend into something that kind of looks like it over time.
- 2:35
And when I'm talking about MCP here, there's kind of two sides to MCP that in my mind feel a bit unrelated. There's this JSON RP-RPC specification, which is really valuable as engineers.
- 2:48
It's like a standard way of sending messages and communicating back and forth between, uh, providers of context for your models and the code that's interacting with the models. And, uh,
- 2:58
getting those messages right is the topic of huge debate on, like, the MCP, um, repos. If you're involved with any standardization process ever, you know how those conversations end going.
- 3:09
And then on the other side, there's this global transport standard, which is the stuff around Streamable HTTP, OAuth 2.1 session management. And global transport standard is hard because you're trying to get everyone to speak the same language, and so it's really nitty, but there's not a lot of, like...
- 3:27
Most of the juice of MCP is in this, the message specification and the way that the, uh, servers are interacting. Um, and so we started asking ourselves, like, "Can we just use MCP for everything?"
- 3:37
And we said yes, with the caveat that yes is for everything involved in presenting model context to models. Um, we have this format where your client is sending these messages.
- 3:51
Something's responding with these messages. Um, where that stream is going, it really doesn't matter. It can be on the same process. It can be another data center. It can be through a giant pile of, uh, enterprise networking stuff.
- 4:06
Um, it doesn't really care at the point that your code is interacting with it. You're just calling a connect to MCP, and you have a, a set of, uh, a set of tools and methods that you can call.
- 4:16
So, uh, standardizing on that seems useful. Um, standard-- Why standardize on anything internally? Um, being boring on stuff like this is good. It's not a competitive advantage to be really good at making Google Drive talk to your app.
- 4:33
It's just a thing that you need to do. Um, it's not your differentiator. Uh, having a single approach to learn as engineers makes things faster. You can spend your cycles working on interesting problems instead of trying to figure out how to plumb, uh, integration.
- 4:49
And, uh, if you're using the same thing everywhere, then, like, each new integration might clean up the field a bit for the next person who comes along. Um, it's, it's ov-overall a good thing i-in cases like this where we're, we're not really doing anything interesting.
- 5:01
We're plumbing context between integrations and things that are consuming the integrations. Uh,
- 5:07
why standardize on MCP internally? Um, I-- This is where I might make an argument to everyone that there's already ecosystem demand. You have to implement MCP because everyone's implementing MCP, so why do two things?
- 5:19
Um, it's becoming an industry standard. There's a large coalition of engineers and organizations that are all involved in building out the standard. Uh, all of the major AI labs are represented in that, so you, you know that as new model capabilities start to be developed, uh, those patterns will be added to the protocol because all the labs
- 5:40
want you to use their features. So I think the standardizing on MCP internally for this type of context is a, is a good bet. And one of the things you get with MCP is that it solves problems that you haven't actually run into yet.
- 5:51
Like, there's a bunch of stuff in the protocol that exists because there's a problem and a need, and having those solutions at hand when you run into them is really important.
- 6:00
So sampling, an example of where this might be valuable in your company, you might have four products that have four different billing models, uh, for reasons because you're building fast.
- 6:09
Um, you might have a bunch of different token limits. You might have different ways of tracking usage. This is really painful 'cause you wanna write one- Integration service to connect to your slides and how do you go and, like, hook the billing and the tokens up correctly?
- 6:24
And MCP has, uh, already has sampling primitives, so you can build your integration. You can just be like, okay, your integration sends a sampling request over the stream. Uh, the other end of the pipe fulfills that request.
- 6:36
You can go and hook it in. Everything works great. And so this is a thing that, uh, uh, a shape problem that might take you a bunch of effort, uh, internally without this, but you already have the answer kind of gift-wrapped for you in the protocol.
- 6:50
And so at Anthropic, we're running into some requirements converging. We're starting to see external remote MCP services popping up like mcp.asana.com, which is really cool. We wanted to be able to talk to those.
- 7:01
Talking to those is complex because you need external network connectivity, you need authentication. Uh, there's a proliferation of internal agents. People have started building, um, PR review bots and, like, Slack management things and just lots of people have lots of ideas.
- 7:19
No one's really sure what's gonna hit, so we're having a huge explosion of LLM-backed services internally. Uh, with that explosion, there's a bunch of security concerns where, uh,
- 7:30
you don't really want all of those services to be going and accessing user credentials, uh, y- because that ends up being an, being kind of a nightmare. You don't want, uh, outbound external network connectivity everywhere.
- 7:43
Um, auditing becomes really complex. Uh, and so we are looking at this problem. We wanted to be able to build our integrations once and use them anywhere. And so, uh, a model I was introduced to by a mentor of mine and a while ago is the pit of success, which is the idea that, um, if you make
- 8:03
the right thing to do the easiest thing to do, then everyone in your org kind of falls into it. And so, uh, we designed a service which is just a piece of shared infrastructure called the MCP gateway that provides a single point of entry and provided engineers just with a connect to MCP call that returns a MCP
- 8:24
SDK client session on the end. And we're trying to make that as simple as possible, uh,
- 8:30
because that way people will use it if it's the easiest thing to do. Um, we used URL-based routing to route to external servers, internal servers. It doesn't matter. It's all the same call.
- 8:40
Uh, we handle all the credential management automatically because you don't wanna be implementing OAuth five times in your company. Uh, it gives you a centralized place for rate limiting and observability.
- 8:51
Uh, I have an obligatory diagram here of a bunch of lines going in and out, but, uh, [laughs] here's a, a gateway in the middle. This is kind of the thing, just one more box will solve all our problems.
- 9:01
Uh, can I go next? Uh, where is my...
- 9:08
Yeah. Uh, so the, uh, the code that we have here, we just made some client libraries where you just MCP gateway connect to MCP. Uh, we pass in a URL, an org ID, account ID.
- 9:20
This is, like, a bit simplified. We actually pass a signed token to authenticate 'cause it's accessing credentials, but this is the basic idea. And then importantly, this call returns an MCP SDK object, which means that when new features get added to the protocol, you just update your MCP packages internally.
- 9:37
You get those features across the board. Everything works great. The same code seamlessly connects to internal, external integrations. When it comes to transports, uh, and this is a bit high level and hand-wavy because everyone's setup is different, um, internally within your network, it really doesn't matter.
- 9:54
You can do anything you want. We've got the standardized transport for connecting to external MCP servers, um, but really just picking the best thing for your org. So we went and picked, uh, WebSockets for our internal transport.
- 10:10
Uh, and here's just a quick code example. It's nothing special. We just have a WebSocket, uh, that's being opened. We are sending these JSON-RPC blobs back and forth over the WebSocket.
- 10:19
And then if I can make this scroll down,
- 10:24
w- at the end, we just pipe those, uh, read streams and write streams into a MCP SDK client session, and we're good to go. We've got MCP going. Um, you might want to do this with gRPC because you wanna wrap these in some multiplexed transport so you don't have to open one WebSocket per connection.
- 10:41
That's pretty simple. Also, uh, we have read stream, write, write stream at the end. Uh, starting to see a pattern here. You can do, like, Unix socket transport if you want.
- 10:50
You can just have, uh, messages be passed that way. Read stream, write stream at the end. MCP works great. Um, I threw in an enterprise-grade email transport implementation over IMAP, um, which is pretty much the same thing.
- 11:02
You just go through. Here is our s- server. We're sending emails back and forth. Uh, "Dear server, I hope this finds you well." [laughing] Uh, "MCP request start," and then we pipe those into a client session at the end.
- 11:15
And so it truly doesn't matter. Like, whatever it takes inside your organization is great. We set up this unified authentication model where we're handling OAuth at the gateway, uh, which means that consumers don't have to worry about all that, uh, all that complexity in their apps.
- 11:33
Uh, we added a get OAuth authorization URL function and a complete OAuth flow because you might have different endpoints. At Anthropic, we have api.anthropic.com, and we have claude.ai, and we might want those redirects to go back to different places.
- 11:45
But, uh, this is tied on the gateway. It's really easy to start a new authentication. Uh, a real advantage of having this put on your gateway is that the credentials are portable.
- 11:55
If you have a batch job that you're kicking off, um, your users don't have to reauthenticate to that. You're just calling the same MCP with your internal user ID, and they get everything added correctly.
- 12:05
Your also internal servers don't have to worry about your tokens.
- 12:10
Um, so your request comes in internally for us. We're hitting a WebSocket connection to MCP gateway, uh, with- Auth token provided as headers to that. Uh, that gateway receives your stored credentials.
- 12:22
You create an authenticated SDK client. You just pass in the bearer token to the auth header, uh, and then you're good to go. The MCP client receives a read stream and a write stream, and so you just plumb those read stream and write stream into your internal transport, and you're, and you're, you're good to go.
- 12:41
Uh, one of the things that this gives for your org that's not immediately obvious but is really valuable is a central place for all of your context that your models are asking for and all the context that's flowing into your models for your org.
- 12:56
Uh, there's some papers written on MCP prompt injection attacks. There is a general risk of, uh, models going and having access to Google Drive and deleting everything in Google Drive.
- 13:09
There's some need of, uh, enforcing policy. You might want to be able to, like, ban malicious servers, um, do some content classification on the request, see what's coming in, kind of give an audit.
- 13:23
And the really nice thing about this is that because it's MCP, all of your messages are in a standardized format, so it's really easy to hook into that stream and be like, "Okay, here is my tool execution message processor," or, "Here is my tool definition thing," or, "Here's my resource management."
- 13:37
And so the payoff that you get from this is, um, adding MCP support to new services is as simple as possible. You just go and import a package. Uh, it doesn't matter what language you're in.
- 13:48
We've got multiple languages internally. They all have their own kind of packages. Engineers can focus on building features and not plumbing. Uh, you have the operational simplicity of having a single point of ingress, egress, and standardized message formats.
- 14:01
And you get future features for free. As the protocol evolves, you get all of that work, uh, naturally.
- 14:08
And so just wanted to go through some takeaways from this that I, I want to p-put to you is that MCP is really just JSON streams, and how you pipe those streams around your infrastructure is a small implementation detail.
- 14:22
It's a couple of lines of code to hook the stream into the client SDK that makes the messages. Uh, the, uh, you should standardize on something, anything. I think MCP is a good idea.
- 14:34
If you don't think it's a good idea, like, just pick something. Um, your future self will thank you. Uh, build some pits of success. You really want to make the right way to do a thing the easiest way to do a thing, and then everyone just falls into doing the right thing naturally.
- 14:46
And also centralizing at the correct layer, so solving some shared problems like auth and external connectivity once allows you to spend your time working on, uh, more interesting problems that are more valuable to you and your, your business.
- 15:00
Uh, thanks. That's all I got for you. Uh, thank you so much [audience applauding] for coming out. [upbeat music]