MCP Client Integration

Angular 21 + Model Context Protocol — connect to an MCP server, discover tools, and invoke them live

What Is MCP?

The Model Context Protocol (MCP) is an open standard that lets AI models discover and call tools exposed by a server — things like database queries, API calls, or any custom logic. Think of it as a structured handshake between a client (your app) and a capability provider (the MCP server).

  • Transport: JSON-RPC 2.0 over HTTP POST — plain requests, no magic.
  • Handshake:initializenotifications/initializedtools/list
  • Invocation:tools/call with a tool name and typed arguments
  • Session: The server issues an Mcp-Session-Id response header; include it in every subsequent request.

This page talks to the mcp-server/ Node.js server included in this repo. Start it with npm start from that folder, then click Connect below.

Angular Service Pattern

// mcp-client.service.ts  (simplified)
import { inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class McpClientService {
  private http = inject(HttpClient);
  private idCounter = 0;

  sessionId  = signal<string | null>(null);
  connected  = signal(false);
  tools      = signal<McpTool[]>([]);

  async connect(serverUrl: string) {
    // ① Initialize — server responds with its capabilities + session ID header
    const initResp = await firstValueFrom(
      this.http.post(serverUrl, {
        jsonrpc: '2.0', method: 'initialize', id: ++this.idCounter,
        params: { protocolVersion: '2024-11-05', capabilities: {},
                  clientInfo: { name: 'my-client', version: '1.0.0' } },
      }, { observe: 'response' })
    );
    const sid = initResp.headers.get('mcp-session-id');
    this.sessionId.set(sid);

    // ② Send required handshake notification (no response expected)
    await firstValueFrom(
      this.http.post(serverUrl,
        { jsonrpc: '2.0', method: 'notifications/initialized' },
        { headers: { 'Mcp-Session-Id': sid! } }
      )
    );

    // ③ Discover tools
    const listResp = await firstValueFrom(
      this.http.post(serverUrl,
        { jsonrpc: '2.0', method: 'tools/list', params: {}, id: ++this.idCounter },
        { headers: { 'Mcp-Session-Id': sid! } }
      )
    );
    this.tools.set((listResp as any).result.tools);
    this.connected.set(true);
  }

  async callTool(serverUrl: string, name: string, args: Record<string, unknown>) {
    const resp = await firstValueFrom(
      this.http.post(serverUrl, {
        jsonrpc: '2.0', method: 'tools/call', id: ++this.idCounter,
        params: { name, arguments: args },
      }, { headers: { 'Mcp-Session-Id': this.sessionId()! } })
    );
    // Result text lives at: resp.result.content[0].text
    return (resp as any).result.content[0].text as string;
  }
}

Live Demo

MCP Server URL (make sure the server is running on port 3001):

Not connected