Day 3/15 — MCP Fundamentals: Client, Server, Tools & Resources
Before we connect Claude to Salesforce, we open up the protocol in the middle. Learn how MCP hosts, clients and servers talk, what changed in revision 2026-07-28, and build a 40-line MCP server you can inspect message by message.

Yesterday we followed one of Neha's questions from Claude to Salesforce and back. Somewhere between her question and Acme Global Tech's opportunities, Claude finds a tool called soqlQuery, works out what arguments it takes, calls it, and reads the result. Nobody writes code for that particular question. Claude and Salesforce simply both speak the same protocol.
That protocol is the Model Context Protocol (MCP), and it is the part of Salesforce Headless 360 (now called AIforce) that people most often treat as magic. It isn't. It is a small set of JSON-RPC messages with clear rules, and once you have watched them go back and forth, the hosted MCP servers stop being a black box. You will also debug them much faster.
The specification changed in July. Revision 2026-07-28 removed the handshake that most tutorials still describe, so part of today is knowing which version a guide is talking about.
By the end of today you will have built and run a 40-line MCP server on your own machine, listed and called its tool from a client and from MCP Inspector, and read the exact messages that revision 2026-07-28 sends.
Where we are
On Day 2 you followed one request through Headless 360: client, identity, access layer, platform, response. MCP is the language spoken in the access step when the client is an AI application. Today is protocol only, with no Salesforce org needed. On Day 4 we switch on the real hosted servers.
MCP in one picture
The specification describes three roles: "The protocol uses JSON-RPC 2.0 messages to establish communication between: Hosts: LLM applications that initiate connections; Clients: Connectors within the host application; Servers: Services that provide context and capabilities".

In Neha's case:
- The host is Claude, the application she types into. It owns the conversation and the model.
- The client lives inside the host, one per server connection. It sends requests such as "list your tools" and "call this tool with these arguments".
- The server is Salesforce's hosted
sobject-allendpoint. It publishes tools and runs them.
The model sits in the host. It never opens a connection to Salesforce; it asks the host to call a tool, and the host's client sends the request. Salesforce's side has no model at all: its FAQ says the hosted server's processing "is completely deterministic" and "The LLM is entirely on the client side".
Transports: how the bytes travel
MCP defines two standard transports:
- stdio: "newline-delimited messages over the standard streams of a client-launched subprocess." The client starts the server as a local process and talks to it through standard input and output. Today's server uses this.
- Streamable HTTP: "each message is an HTTP POST to a single MCP endpoint; replies arrive as a JSON object or a request-scoped SSE stream." Remote servers use this; Salesforce's own Claude Code instructions add its hosted servers with
--transport http.
An older HTTP+SSE transport is now classified as deprecated, with the advice "Migrate to Streamable HTTP." If a tutorial talks about an SSE endpoint for a new server, it is out of date.
The transport matters in practice. Claude Code can launch a local stdio server. Claude's API connector can't: its documentation says the server "must be publicly exposed through HTTP" and "Local STDIO servers cannot be connected directly." Keep that in mind when you plan where a server runs.
The three server primitives, and what Salesforce uses
Servers can offer three kinds of things. In the specification's words:
| Primitive | Specification definition | Who uses it | Salesforce hosted servers |
|---|---|---|---|
| Tools | "Functions for the AI model to execute" | The model decides when to call them | Yes: sobject-all has 11, sobject-reads has 6 |
| Resources | "Context and data, for the user or the AI model to use" | The application attaches them to context | Not documented for the standard servers |
| Prompts | "Templated messages and workflows for users" | The user picks them | sobject-all "Also includes prompt templates (currently usable in Claude and Cursor)", and Setup listed 2 prompts for it, though none appeared in claude.ai in my org |
Tools do nearly all the work in this series. A tool has a name, a description and an input schema, and the model reads all three to decide whether and how to call it. That is why tool descriptions are written for a model, not for a human skimming a list, and why Day 12 spends time on the descriptions of our Apex tools.
Custom hosted servers can add more: Salesforce's Summer '26 guide lists "Prompt Builder: Expose prompts from Prompt Builder as MCP prompts" as one custom source. Resources aren't documented for Salesforce's standard servers, so this series doesn't rely on them.
Clients can offer features back to servers too. In revision 2026-07-28 the list is short: "Elicitation: Server-initiated requests for additional information from users." Roots, Sampling and Logging are deprecated.
The request lifecycle in revision 2026-07-28
This is the part that changed. The current specification, revision 2026-07-28, lists as its first change: "Make MCP stateless: remove the initialize/notifications/initialized handshake. Every request now carries its protocol version and client capabilities in _meta".

A typical exchange has three requests:
server/discover(optional for the client). "Servers MUST implement this RPC to advertise their supported protocol versions, capabilities, and identity." A client can call it first to learn what the server supports, but the spec also allows a client to go straight to other requests.tools/list. The server returns each tool's name, title, description, input schema and, optionally, an output schema and annotations.tools/call. The client sends a tool name and arguments. The server runs the tool and returns content. If the tool fails in a way the model can fix, such as a bad argument, the result carriesisError: trueand a message the model can read.
Every one of those requests carries the same small envelope in params._meta: the protocol version (io.modelcontextprotocol/protocolVersion), the client's capabilities (io.modelcontextprotocol/clientCapabilities) and, usually, the client's name and version (io.modelcontextprotocol/clientInfo). The first two are required. A request without them "is malformed; the server MUST reject it with JSON-RPC error code -32602". You will see exactly that error in the hands-on.
Why go stateless? Because a server no longer has to remember anything about a connection. Any request can land on any server instance, which is how you want a large hosted service to behave. On Streamable HTTP, the change also removed "protocol-level sessions and the Mcp-Session-Id header" and added required Mcp-Method and Mcp-Name headers on each POST.
What changed from 2025-11-25
MCP articles written before this revision describe 2025-11-25 or earlier. The differences that matter for this series:
- The
initializerequest andnotifications/initializedhandshake are gone. Version and capabilities travel in each request's_meta. server/discoveris new, and servers must implement it.- Protocol-level sessions and the
Mcp-Session-Idheader are removed. - Server-initiated requests such as
sampling/createMessageandelicitation/createare replaced by Multi Round-Trip Requests: the server returns aninput_requiredresult and the client retries with the answers. - Roots, Sampling and Logging are deprecated. So is Dynamic Client Registration, in favour of Client ID Metadata Documents.
If a guide starts with initialize, it is describing 2025-11-25 or earlier. That is still useful, because one server can support both revisions.
Which revision do Salesforce's hosted servers speak? Salesforce doesn't document it, so I checked. On 2026-10-04, in a Developer Edition org, MCP Inspector 2.9.0 connected to the hosted sobject-reads server over Streamable HTTP and the server card showed MCP 2025-06-18: an older revision than either of the two above, with the initialize handshake. That is an observation on one date, not a promise. Salesforce can move to a newer revision without notice, so don't build anything that depends on it. The specification calls an implementation that supports both the old handshake and the new per-request model "dual-era", and the server you build today is one; a client that can still speak the handshake works with both.

Authorization in MCP
A local stdio server doesn't need OAuth. The specification says stdio implementations "SHOULD NOT" follow the authorization spec and should "instead retrieve credentials from the environment". Remote servers are different.
"Authorization is OPTIONAL for MCP implementations," says the specification, and Salesforce's security blog repeats the point. Salesforce made it mandatory for its hosted servers. The rules a protected server follows are standard OAuth:
- A protected MCP server "acts as an OAuth 2.1 resource server".
- "MCP servers MUST implement OAuth 2.0 Protected Resource Metadata (RFC9728). MCP clients MUST use OAuth 2.0 Protected Resource Metadata for authorization server discovery." That is the public document you read on Day 1, which lists
login.salesforce.comand the scopesmcp_apiandrefresh_token. - Clients "MUST implement Resource Indicators for OAuth 2.0" (RFC 8707), and the authorization flow uses PKCE.
- For registering the client, the spec now prefers Client ID Metadata Documents and calls Dynamic Client Registration "deprecated and retained for backwards compatibility". Clients can also be pre-registered.
Salesforce takes the pre-registration route: "Dynamic client registration is not supported. Administrators must explicitly create and configure External Client Apps". That is why Claude will ask you for a consumer key on Day 6, and why Day 5 is all about creating that app.
Hands-on: build and inspect a local MCP server
You will build a server with one tool, get_local_time. Acme Global Tech is in India, so the tool returns the current time in any IANA zone, such as Asia/Kolkata, before Neha proposes a call time. No Salesforce yet: the point is to see the protocol with nothing else in the way. I ran every command below on September 30, 2026 with @modelcontextprotocol/server 2.2.0, @modelcontextprotocol/client 2.2.0, MCP Inspector 2.8.0 and mcp 2.2.0 for Python.
Step 1: create the project
The SDK and the client script run on Node.js 20 or later. MCP Inspector (Step 5) needs Node.js 22.19 or later, the version this series recommends anyway.
mkdir acme-time-mcp && cd acme-time-mcp
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/server@2.2.0 @modelcontextprotocol/client@2.2.0 zod
npm install -D tsx
type=module matters: the files use top-level await, which Node only allows in ES modules. The SDK's README describes v2 as "implementing the 2026-07-28 MCP spec". The older single package, @modelcontextprotocol/sdk (1.31.0 today), still receives fixes "for at least 6 months after v2's release", so you will meet both in the wild.
Step 2: write the server
Create server.ts:
// server.ts: one read-only tool. MCP TypeScript SDK v2 (@modelcontextprotocol/server 2.2.0).
import { McpServer } from "@modelcontextprotocol/server";
import { serveStdio } from "@modelcontextprotocol/server/stdio";
import * as z from "zod";
function createServer(): McpServer {
const server = new McpServer({ name: "acme-time", version: "1.0.0" });
server.registerTool(
"get_local_time",
{
title: "Get local time",
description:
"Returns the current date and time in one IANA time zone, for example Asia/Kolkata. " +
"Use it before proposing a meeting time to a customer. Read-only.",
inputSchema: z.object({
timeZone: z.string().describe("IANA time zone name, for example Asia/Kolkata"),
}),
outputSchema: z.object({ timeZone: z.string(), localTime: z.string(), utc: z.string() }),
annotations: { readOnlyHint: true, openWorldHint: false },
},
async ({ timeZone }) => {
const now = new Date();
let localTime: string;
try {
localTime = new Intl.DateTimeFormat("en-GB", { timeZone, dateStyle: "full", timeStyle: "short" }).format(now);
} catch {
const text = `Unknown time zone: ${timeZone}. Use an IANA name such as Asia/Kolkata.`;
return { isError: true, content: [{ type: "text", text }] };
}
const output = { timeZone, localTime, utc: now.toISOString() };
return { content: [{ type: "text", text: JSON.stringify(output) }], structuredContent: output };
},
);
return server;
}
// One server per connection. Answers server/discover (2026-07-28) and initialize (2025-11-25).
serveStdio(createServer);
Four things in there are worth a second look:
- The description is written for a model. It says what the tool does, when to use it and that it is read-only. The model sees this text and nothing else about your code.
- The schemas are real JSON Schema on the wire. The SDK turns the zod objects into the
inputSchemaandoutputSchemathattools/listreturns. - Annotations are hints.
readOnlyHint: truetells clients the tool doesn't change anything. Clients may use that to skip a confirmation, but the specification treats annotations as untrusted unless the server is trusted, so they never replace permissions. - Errors go back as results. An unknown zone returns
isError: truewith a sentence the model can act on, instead of crashing the request.
If you run npx tsx server.ts on its own, nothing seems to happen. That is correct: a stdio server waits for a client to write to its standard input. Press Ctrl+C and move on.
Step 3: call it from a client
Create client.ts. It starts the server as a subprocess, pins revision 2026-07-28, lists the tools and calls the tool twice:
// client.ts: start server.ts over stdio, pin revision 2026-07-28, list tools, call one.
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
const transport = new StdioClientTransport({ command: "npx", args: ["tsx", "server.ts"] });
const client = new Client(
{ name: "day-3-client", version: "1.0.0" },
{ versionNegotiation: { mode: { pin: "2026-07-28" } } }, // default is the 2025 initialize handshake
);
await client.connect(transport); // sends server/discover, no initialize
console.log("protocol:", client.getNegotiatedProtocolVersion());
const { tools } = await client.listTools();
console.log("tools:", tools.map((tool) => tool.name));
const result = await client.callTool({ name: "get_local_time", arguments: { timeZone: "Asia/Kolkata" } });
console.log("result:", result.structuredContent);
const bad = await client.callTool({ name: "get_local_time", arguments: { timeZone: "Mars/Olympus" } });
console.log("error:", bad.isError, bad.content);
await client.close();
Run npx tsx client.ts. This is what I got:
protocol: 2026-07-28
tools: [ 'get_local_time' ]
result: {
timeZone: 'Asia/Kolkata',
localTime: 'Wednesday, 30 September 2026 at 23:18',
utc: '2026-09-30T17:48:20.129Z'
}
error: true [
{
type: 'text',
text: 'Unknown time zone: Mars/Olympus. Use an IANA name such as Asia/Kolkata.'
}
]
Note the comment on the versionNegotiation line. The v2 client defaults to the old initialize handshake (mode 'legacy'); pinning the version makes it send server/discover instead. I checked both: with the default, the same server answered initialize and notifications/initialized and served the tool just the same. That is what dual-era means in practice.
Step 4: read the raw messages
SDKs hide the protocol, so let's look at it directly. Save these three lines as requests.jsonl. Each line is one JSON-RPC request; the third deliberately leaves out _meta:
{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientInfo":{"name":"terminal","version":"1.0.0"},"io.modelcontextprotocol/clientCapabilities":{}}}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_local_time","arguments":{"timeZone":"Asia/Kolkata"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}
{"jsonrpc":"2.0","id":3,"method":"tools/list","params":{}}
Pipe them into the server. The sleep keeps standard input open long enough for the answers, because the server stops when its input closes:
(cat requests.jsonl; sleep 5) | npx tsx server.ts
You get one JSON line per request (shortened here):
{"result":{"supportedVersions":["2026-07-28"],"capabilities":{"tools":{"listChanged":true}},"resultType":"complete", "_meta":{"io.modelcontextprotocol/serverInfo":{"name":"acme-time","version":"1.0.0"}}, ...},"jsonrpc":"2.0","id":1}
{"jsonrpc":"2.0","id":3,"error":{"code":-32602,"message":"Request is missing the required _meta envelope for protocol revision 2026-07-28 (io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/clientCapabilities)"}}
{"result":{"content":[{"type":"text","text":"{\"timeZone\":\"Asia/Kolkata\", ...}"}],"structuredContent":{...},"resultType":"complete", ...},"jsonrpc":"2.0","id":2}
Three lessons in three lines. server/discover answered with the supported versions, the capabilities and the server's identity. The tools/call worked on its own, with no earlier handshake, because it carried its own _meta. And the request without _meta got -32602, exactly as the specification requires. Notice also that the answers came back out of order: JSON-RPC matches responses to requests by id, not by position.
Step 5: inspect it with MCP Inspector
MCP Inspector is the official debugging tool. It has a web UI (npx @modelcontextprotocol/inspector), a terminal UI and a CLI, and "The Inspector requires Node 22.19.0 or newer". Here is the CLI, which is easiest to paste into an article and a ticket. Run it from the project folder:
npx @modelcontextprotocol/inspector --cli npx tsx server.ts --protocol-era modern --method tools/list
npx @modelcontextprotocol/inspector --cli npx tsx server.ts --protocol-era modern \
--method tools/call --tool-name get_local_time --tool-arg timeZone=Asia/Kolkata
The first prints the tool definition as JSON, including the generated inputSchema and outputSchema. The second prints the result with structuredContent. Two details I learned the hard way:
- Without
--protocol-era modern, the CLI uses the old handshake. I logged what the server received:initializewith2025-11-25by default, andserver/discoverfollowed bytools/listandtools/callwith the flag. Both work against this server, but only one shows you the current protocol. - Put the Inspector's own flags after the server command. With
--protocol-eraplaced beforenpx tsx server.ts, Inspector 2.8.0 answered "No servers found in config file". - A broken npm cache can serve an old Inspector. In one test run,
npx @modelcontextprotocol/inspectorfailed withnpm error code ECOMPROMISED("Lock compromised"), and the cache offered a stale 0.15.0. A fresh cache fixed it:npx --cache "$TMPDIR/npm-cache-inspector" -y @modelcontextprotocol/inspector@lateststarted Inspector 2.9.0.
On Day 7 you point the same tool at a Salesforce hosted server, with OAuth. One trap to know now: Inspector 2.9.0 (the version with a Servers list and an Add Servers button) sent the callback http://127.0.0.1:6274/oauth/callback (127.0.0.1, not localhost), and Salesforce answered redirect_uri_mismatch. Adding that callback to the app doesn't work either: Salesforce refuses to save it ("Cannot be an HTTP URL"), because only localhost may use plain http. Open the Inspector at http://localhost:6274 instead, and the localhost callback you register on Day 5 matches. That is how I saw the protocol version above.
Python version
The official Python SDK is the mcp package, version 2.2.0, which its README calls "v2 of the MCP Python SDK, the current stable release line." This is the same server and a client that pins 2026-07-28 (Python 3.10+). I ran both with Python 3.13.
python3 -m venv .venv && source .venv/bin/activate
pip install "mcp==2.2.0"
"""server.py: the same one-tool MCP server with the MCP Python SDK v2 (mcp 2.2.0). Run: python server.py"""
from datetime import datetime, timezone
from typing import Annotated, TypedDict
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from pydantic import Field
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError
from mcp.types import ToolAnnotations
mcp = MCPServer("acme-time", version="1.0.0")
class LocalTime(TypedDict):
timeZone: str
localTime: str
utc: str
@mcp.tool(
title="Get local time",
annotations=ToolAnnotations(read_only_hint=True, open_world_hint=False),
)
def get_local_time(
timeZone: Annotated[str, Field(description="IANA time zone name, for example Asia/Kolkata")],
) -> LocalTime:
"""Returns the current date and time in one IANA time zone, for example Asia/Kolkata.
Use it before proposing a meeting time to a customer. Read-only."""
try:
zone = ZoneInfo(timeZone)
except (ZoneInfoNotFoundError, ValueError):
raise ToolError(f"Unknown time zone: {timeZone}. Use an IANA name such as Asia/Kolkata.")
now = datetime.now(timezone.utc)
local = now.astimezone(zone)
return {
"timeZone": timeZone,
"localTime": local.strftime("%A, %d %B %Y at %H:%M"),
"utc": now.isoformat(timespec="seconds"),
}
if __name__ == "__main__":
mcp.run() # stdio by default
"""client.py: launch server.py over stdio, pin revision 2026-07-28, list tools and call one."""
import asyncio
from mcp import Client
from mcp.client.stdio import StdioServerParameters
async def main() -> None:
server = StdioServerParameters(command="python", args=["server.py"])
async with Client(server, mode="2026-07-28") as client:
tools = await client.list_tools()
print("tools:", [tool.name for tool in tools.tools])
result = await client.call_tool("get_local_time", {"timeZone": "Asia/Kolkata"})
print("result:", result.structured_content)
bad = await client.call_tool("get_local_time", {"timeZone": "Mars/Olympus"})
print("error:", bad.is_error, bad.content[0].text if bad.content else None)
asyncio.run(main())
Run python client.py. The Python Client defaults to mode="auto", which tries server/discover and falls back to initialize; the pin makes the choice explicit. Raise ToolError for failures you expect: the call returns is_error=True with your message in content for the model to read. The Inspector works the same way: npx @modelcontextprotocol/inspector --cli python server.py --protocol-era modern --method tools/list.
What can go wrong
These are the mistakes I see most when people first build or connect MCP servers.
| Symptom | Cause | Fix |
|---|---|---|
npx tsx server.ts starts and does nothing |
A stdio server waits for a client on standard input | Run it through a client, the Inspector or the pipe in Step 4 |
| A client that takes a URL can't use your local server | The client only supports remote HTTP servers; Claude's API connector says "Local STDIO servers cannot be connected directly" | Use a client that launches stdio servers, such as Claude Code, or serve over Streamable HTTP |
Your client still sends initialize |
Both the v2 TypeScript client and the Inspector CLI default to the 2025 handshake | Pin 2026-07-28 in code, or pass --protocol-era modern after the server command |
| The model calls the wrong tool, or passes the wrong arguments | The description says what the code does, not when to use it | Write descriptions for the model: purpose, when to call, constraints, an example value |
| Responses are huge and the model loses the thread | The tool returns everything it can find | Return only what the question needs. Salesforce gives its own soqlQuery the same advice: "Always include a WHERE clause to filter results and a LIMIT clause to control result size." |
Inspector's sign-in to Salesforce ends in redirect_uri_mismatch |
Inspector 2.9.0 was opened at 127.0.0.1, so its callback doesn't match the app's localhost one |
Open http://localhost:6274; don't add an http://127.0.0.1 callback, which Salesforce refuses |
| Random parse errors from a stdio server | Log lines written to standard output, which carries the protocol messages | Log to standard error, never to standard output |
Security note: who runs what
A stdio server is a program the client launches on your machine, with your user's access to files and network. Install local servers the way you would install any command-line tool: only from sources you trust. For remote servers, the specification's rule applies: "Hosts must obtain explicit user consent before invoking any tool", and annotations "should be considered untrusted, unless obtained from a trusted server." Salesforce's hosted servers add their own boundary on top, which you already know from Day 2: every call runs as the signed-in user.
Today's checklist
- I can name the three MCP roles (host, client, server) and say which one Claude, and which one Salesforce, plays.
- I know the difference between the stdio and Streamable HTTP transports.
- I can explain tools, resources and prompts, and which of them Salesforce's standard servers document.
- I can list what revision 2026-07-28 removed compared with 2025-11-25.
- My local server answered
server/discover,tools/listandtools/call. - I saw the
-32602error for a request without_meta. - I inspected the server with MCP Inspector using
--protocol-era modern, or ran the client script if I'm on Node 20.
Frequently asked questions
What is the difference between an MCP host, client and server?
The host is the AI application the user talks to, such as Claude. It contains one MCP client per server connection. The server publishes tools, resources or prompts. Salesforce's hosted MCP servers are servers; Claude is the host and client.
Which MCP protocol version do Salesforce's hosted servers use?
Salesforce doesn't document it. On 2026-10-04, MCP Inspector 2.9.0 showed the hosted sobject-reads server negotiating MCP 2025-06-18 in a Developer Edition org, older than the current 2026-07-28 and the previous 2025-11-25. That can change at any time, and Salesforce lists Claude as a tested client, so you don't need the answer to connect.
Do I need an MCP SDK to use Salesforce's hosted servers?
No. Salesforce runs the servers and Claude is the client. You need an SDK only to build your own server or client. Today's server is practice for reading tools, schemas and errors, which you will do against Salesforce on Day 7.
Do Salesforce's hosted servers expose MCP resources?
Salesforce documents tools on its standard servers, plus prompt templates on sobject-all, which it says work in Claude and Cursor; on 2026-10-04 no Salesforce prompt templates appeared anywhere in claude.ai. Resources aren't documented for the standard servers, so this series doesn't rely on them.
Should I use version 1 or version 2 of the SDKs?
Use v2 for new work: the TypeScript packages @modelcontextprotocol/server and @modelcontextprotocol/client implement revision 2026-07-28, and so does mcp 2.x for Python. Version 1 of the TypeScript SDK still gets bug fixes and security updates for at least six months after v2's release.
Can I use today's server from Claude Code?
Yes, in principle. Claude Code adds local stdio servers with claude mcp add <name> -- <command> [args...], where the double dash separates Claude's options from the command that starts the server. I didn't include that in today's tested steps, because it changes your Claude Code settings.
What's next
You now know what an MCP server is, because you have built one. Tomorrow, on Day 4, we switch on the servers Salesforce runs for you: where they live in Setup, which one to start with, what sobject-reads allows compared with sobject-all, and how to copy the right URL for your org.
Sources
Verified against the sources below on September 30, 2026. Salesforce ships Headless 360 changes often: check the linked docs if a screen looks different.
- Model Context Protocol specification, revision 2026-07-28
- Key Changes since 2025-11-25 (MCP changelog)
- Discovery: server/discover (MCP specification)
- Authorization (MCP specification)
- MCP TypeScript SDK — v2 packages @modelcontextprotocol/server and @modelcontextprotocol/client 2.2.0
- MCP Inspector
- FAQ (forcedotcom/mcp-hosted wiki) — no LLM on the Salesforce side
Everything from today on one page. Tap to zoom, or download it for later.
All 15 days in this series
- Day 01What Is Salesforce Headless 360 and Why Does It Matter?
- Day 02Headless 360 Architecture Explained
- Day 03MCP Fundamentals: Client, Server, Tools & Resources
- Day 04Enable & Configure the Salesforce MCP Server





Comments
Loading comments...