For more details, please refer to my article:
- Get Start with Model Context Protocol.
- MCP Server and Client with SSE & The New Streamable HTTP
- MCP Authorization: The NEW RS and AS Separation!
Local MCP with stdio
An MCP server that exposes a create-mcp-server-app tool to MCP hosts.
By running this tool, either with Claude for Desktop or some other host, a new project for creating an MCP server with necessary set ups and sample code will be created in the desktop folder.
Sample Code will include the following.
- creating a server instance
- defining available tools
- handling tools execution
- starting the server
- Run
npm installto install necessary dependency - Run
npm run buildto build the project build/index.jswill the server path to set up in the MCP host
- Download Claude for Desktop
- Open the configuration file at
~/Library/Application Support/Claude/claude_desktop_config.json- If the file does not exist, create one with the same name
- Add server configuration
{
"mcpServers": {
"create-mcp-app": {
"command": "node",
"args": [
"/absolute-path-to/build/index.js"
]
}
}
}
- Save the file and restart Claude for Desktop.
- Enter the prompt create a new mcp server project. You should see a sample MCP Server project created at desktop directory with some responses indicating the success.
An OpenAI-chatbot host that uses MCP clients to connect to MCP servers.
- Set up
OPENAI_API_KEYin.env. - Add MCP server configurations to connect the host in
server-config.json.
{
"some-server-name-1": {
"command": "node",
"args": [
"/absolut-path-to-local-server-folder-1/build/index.js"
]
},
"some-server-name-2": {
"command": "uv",
"args": [
"--directory",
"/absolut-path-to-local-server-folder-2",
"run",
"main.py"
]
}
}
- Run
npm installto install necessary dependency - Run
npm run buildto build the project - Start client by running
node build/index.js
Remote MCP with SSE
An MCP server that is able to connect to multiple client with SSEServerTransport.
- Run
npm installto install necessary dependency - (Optional) Change the endpoint url. By default, the endpoint for connection will be
/connect, and that for messaging will be/messages. - Run
npm run buildto build the project - Start server by running
node build/index.js. This will start a localhost listenining to port 3000.
An MCP client connect to remote server with SSEClientTransport.
- Run
npm installto install necessary dependency - (Optional) Change the server url. By default, it will use
http://localhost:3000/connect. - Run
npm run buildto build the project - Start client by running
node build/index.js.
Remote MCP with Streamable HTTP
An MCP server that is able to connect to multiple client with StreamableHTTPServerTransport.
This server supports:
- Basic functionalities, ie: Client establishing connections and sneding messages (or requests such as list tools, list resources, call tools and etc.) to the server and server responding.
- Standalone SSE to open an SSE stream to support server-initiated messgaes
- Tools
- A regular tool that return a single response
- A tool that sends multiple messages back to the client with notifications
- Run
npm installto install necessary dependency - (Optional) Change the endpoint url. By default, the endpoint will be
/mcp. - Run
npm run buildto build the project - Start server by running
node build/index.js. This will start a localhost listenining to port 3000.
An MCP client connect to remote server with SSEClientTransport.
Upon start, the client will
- Connect to the server
- Set up notifications to receive update on Logging messages and tool changes
- List tools and call tools
- Run
npm installto install necessary dependency - (Optional) Change the server url. By default, it will use
http://localhost:3000/mcp. - Run
npm run buildto build the project - Start client by running
node build/index.js.
This demo is created conforming the new MCP Authorization specifciation where the Authorization Server and Resource server are seperated and the flow consists of four roles
- End user
- MCP Client
- MCP Resource Server
- Authorization Sever
An OAuth2 authorization server with dynamic client registration implemented using the node-oauth/oauth2-server library.
NOTE: For simplification, instead of an actual database, runtime variables are used in the example to store code/token/user data.
Basic implementations for the following endpoints are provided conforming to the IETF specifications.
- Authorization endpoint
- on
GET, shows a simple login form to collect user information that willPOSTto the same endpoint to start authorization flow.
- on
- Token endpoint (for both authorization code exchange and refresh token exchange)
- Access token generated will be JWT Tokens conforming to JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens
- Registration endpoint for dynamic client registration
- Revocation endpoint for revoking access tokens and refresh tokens
/.well-known/oauth-authorization-serverfor Server Metadata discovery
- Error endpoint to display errors related to client's
redirect_uri
A Remote MCP Resource Server using StreamableHTTPServerTransport.
/.well-known/oauth-protected-resourceend point for Protected Resource Metadata discovery- requires Authorization in order to access the following supported features.
- Basic functionalities, ie: Client establishing connections and sneding messages (or requests such as list tools, list resources, call tools and etc.) to the server and server responding.
- Tools
- A regular tool that return a single response
- A tool that sends multiple messages back to the client with notifications
The access token required will be in the form of an JWT Token and the validation is performed by checking the signature, header, and payloads based on the specification(RFC9068).
Errors are handled based on Error Response specification using the WWW-Authenticate Response Header Field
An MCP client connect to remote server with StreamableHTTPClientTransport and providing an OAuthClientProvider to handle authorization as needed.
By using an OAuthClientProvider, the SDK will
- connect to the server with any existing access token from the provider (
OAuthClientProvider.tokens) if there is any or try to connect without specifying any authorization parameters. - If the access token has expired, and we try to connect or send messages to the server, for example, by calling listTools, the provider is used to refresh the token.
- If token refresh fails or no access token exists, and auth is required,
OAuthClientProvideris then used to start the authorization flow and anUnauthorizedErrorwill be thrown from connect or the function we used to send messages.
Internal authorization flow managed by OAuthClientProvider:
- call
OAuthClientProvider.clientInformationto try to load any existing client id and secret - If not exist and dynamic client registration is supported, try client registration and call
OAuthClientProvider.saveClientInformationto save the registered client information - Call
OAuthClientProvider.redirectToAuthorizationwith the authorization url - After we call
transport.finishAuthwith the authorization code, exchange the code for access token.
Upon start, the client will
- Try to Connect to the server without existing authorization info
- Upon getting an
UnauthorizedErrorandOAuthClientProvider.redirectToAuthorizationcalled, start a local server to receive authorization call back and open the browser with the authorization url - Wait for the user to log in to complete and receive authorization code (or error) within the callback
- Call
StreamableHTTPClientTransport.finishAuthwith the authorization code to have the SDK to exchange the code for an access token to - Upon authorization finish, Reconnect and make some tools calls to confirm the the authorization is successful
For those who want to manage authorization using other libraries, I have also included some sample code on how to perform the enitre authorization flow using openid-client.
Since the current Typescript SDK is not supporting the authorization flow from the newest authorization specification yet, I have forked the repository and added some changes.
PS: I have created a PR for it if anyone is being super nice and wants to give it a review!
To test out the examples in this demo
- Download the auth-client branch of the fork
- In
tsconfig, removedistfromexclude. Ie:"exclude": ["node_modules"] npm run build- Use the build as the dependency
Change the following as needed in .env.
CIPHER_KEYCIPHER_IVCIPHER_KEY_ENCODINGCIPHER_IV_ENCODING
ACCESS_TOKEN_SIGN_ALGORITHMACCESS_TOKEN_SIGN_SECRET
Change the following as needed in .
AUTHORIZATION_SERVER_PORTAUTHORIZATION_SERVER_HOSTNAMEAUTHORIZATION_SERVER_PROTOCOLOAUTH_ROUTER_ENDPOINTAUTHORIZE_ENDPOINTTOKEN_ENDPOINTOTHER_ERROR_ENDPOINTREVOCATION_ENDPOINTREGISTRATION_ENDPOINTAUTH_METHODSGRANT_TYPESMCP_SCOPERESPONSE_TYPESCODE_CHALLENGE_METHODSACCESS_TOKEN_LIFETIMEREFRESH_TOKEN_LIFETIME
RESOURCE_SERVER_PORTRESOURCE_SERVER_PROTOCOLRESOURCE_SERVER_HOSTNAMEMCP_ENDPOINT
CLIENT_SERVER_PORTCLIENT_SERVER_HOSTNAMECLIENT_SERVER_PROTOCOLCLIENT_SERVER_CALLBACK_PATH
Run npm run dev to
- build the project
- start the authorization server
- add some dummy user data (the user that will be logged in to receive access token)
- start the MCP Resource server
- Run the MCP client
- Red: Client
- Blue: Authorization Server
- Green: Resource Server
Run npm run dev:as to
- build the project
- start the authorization server
- add some dummy user data (the user that will be logged in to receive access token)
Run npm run dev:rs to
- build the project
- start the MCP Resource server
Run npm run dev:client to
- build the project
- Run the MCP client
/specificationDiagram.png)