{
  "recordedOn": "2026-09-28",
  "endpoint": "https://thelatentsource.com/mcp",
  "client": "scripts/portfolio/mcp-demo/",
  "description": "A real browser client sends JSON-RPC through a loopback proxy to the public endpoint. Displayed responses come from those requests, not fixtures. No model-generated answer is shown. Playback is recorded and does not call the endpoint.",
  "durationSeconds": 117.44,
  "narration": {
    "model": "gemini-3.1-flash-tts-preview",
    "voice": "Charon",
    "language": "en",
    "segments": [
      {
        "step": "initialize",
        "start": 0.014,
        "text": "This is a small MCP client connected to The Latent Source. The first request initializes the connection. The server identifies itself, negotiates a protocol version, and advertises its tool capability. The client then acknowledges initialization."
      },
      {
        "step": "tools",
        "start": 20.556,
        "text": "The client asks which tools are available. The blog returns three: search articles, get article, and list articles. Each comes with a description and an input schema, so a client can discover what to send."
      },
      {
        "step": "browse",
        "start": 36.916,
        "text": "I start by listing three published articles. The response includes titles, summaries, and source URLs. These are actual results from the public blog, shown here without generating an answer or changing any content."
      },
      {
        "step": "search",
        "start": 53.328,
        "text": "Next, I search for MCP. The client sends a tool call with the query, language, and result limit. The blog returns ranked matches. Each result includes a slug that the client can use to retrieve the article."
      },
      {
        "step": "read",
        "start": 71.083,
        "text": "I pass the first result's slug to get article. The response contains the published article as Markdown, with its metadata returned separately. A client can now read the source text and follow its URL. This is the actual MCP transport, rather than a recording of the site's chat interface."
      },
      {
        "step": "missing",
        "start": 97.904,
        "text": "Finally, I request an article that doesn't exist. The server returns a tool error instead of invented content. That failure stays visible to the client. All three tools are read-only; publishing and administration stay outside this interface."
      }
    ]
  },
  "videoSha256": "d9a9af470fc62c2ee6fe1dca282e2fbad43b37334ac2b4ee13cb32c659952461",
  "requests": [
    {
      "request": {
        "jsonrpc": "2.0",
        "id": 1,
        "method": "initialize",
        "params": {
          "protocolVersion": "2025-06-18",
          "capabilities": {},
          "clientInfo": {
            "name": "latent-video-client",
            "version": "1.0"
          }
        }
      },
      "response": {
        "jsonrpc": "2.0",
        "id": 1,
        "result": {
          "protocolVersion": "2025-06-18",
          "capabilities": {
            "tools": {
              "listChanged": false
            }
          },
          "serverInfo": {
            "name": "latentsource-blog",
            "title": "latentSource Blog",
            "version": "1.0.0"
          },
          "instructions": "Read-only access to latentSource (https://thelatentsource.com), a blog about data science, machine learning, and AI engineering by Oswaldo Orona. Use search_articles to find articles by meaning or keywords, list_articles to browse, and get_article to read one as markdown. Articles are English-first; pass lang='es' for Spanish where available."
        }
      },
      "status": 200,
      "elapsedMs": 329
    },
    {
      "request": {
        "jsonrpc": "2.0",
        "method": "notifications/initialized"
      },
      "response": null,
      "status": 202,
      "elapsedMs": 106
    },
    {
      "request": {
        "jsonrpc": "2.0",
        "id": 2,
        "method": "tools/list"
      },
      "response": {
        "jsonrpc": "2.0",
        "id": 2,
        "result": {
          "tools": [
            {
              "name": "search_articles",
              "title": "Search articles",
              "description": "Hybrid keyword + semantic search over all published latentSource articles (BM25 and embedding similarity fused with reciprocal rank). Best first call when looking for coverage of a topic — returns matching articles with slug, title, summary, tags, and URL.",
              "inputSchema": {
                "type": "object",
                "properties": {
                  "query": {
                    "type": "string",
                    "description": "Natural-language search query",
                    "minLength": 1,
                    "maxLength": 500
                  },
                  "limit": {
                    "type": "integer",
                    "description": "Maximum results (1-20)",
                    "minimum": 1,
                    "maximum": 20,
                    "default": 5
                  },
                  "lang": {
                    "type": "string",
                    "enum": [
                      "en",
                      "es"
                    ],
                    "default": "en",
                    "description": "Language to search in"
                  }
                },
                "required": [
                  "query"
                ]
              }
            },
            {
              "name": "get_article",
              "title": "Get article",
              "description": "Fetch one published article as markdown by slug (find slugs via search_articles or list_articles). The text content is the full article; metadata comes back as structured content.",
              "inputSchema": {
                "type": "object",
                "properties": {
                  "slug": {
                    "type": "string",
                    "pattern": "^[\\w\\-]+$",
                    "description": "Article slug, e.g. 'navigating-latent-space'"
                  },
                  "lang": {
                    "type": "string",
                    "enum": [
                      "en",
                      "es"
                    ],
                    "default": "en",
                    "description": "Return the Spanish translation when available"
                  }
                },
                "required": [
                  "slug"
                ]
              }
            },
            {
              "name": "list_articles",
              "title": "List articles",
              "description": "List published articles newest-first, optionally filtered by tag. Returns slug, title, summary, tags, published date, and URL.",
              "inputSchema": {
                "type": "object",
                "properties": {
                  "tag": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Only articles carrying this tag"
                  },
                  "limit": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 20
                  }
                },
                "required": []
              }
            }
          ]
        }
      },
      "status": 200,
      "elapsedMs": 264
    },
    {
      "request": {
        "jsonrpc": "2.0",
        "id": 3,
        "method": "tools/call",
        "params": {
          "name": "list_articles",
          "arguments": {
            "limit": 3
          }
        }
      },
      "response": {
        "jsonrpc": "2.0",
        "id": 3,
        "result": {
          "content": [
            {
              "type": "text",
              "text": "{\n  \"articles\": [\n    {\n      \"slug\": \"reactreel-media-agent\",\n      \"title\": \"ReActReel: finding a movie scene or song from a remembered line\",\n      \"summary\": \"A Gemini agent searches for movie scenes and songs, shows its tool calls, and returns a YouTube result inside the app.\",\n      \"tags\": [\n        \"AI agents\",\n        \"ReAct\",\n        \"Gemini\",\n        \"Tool calling\"\n      ],\n      \"published\": \"2026-09-22\",\n      \"reading_time\": \"2 min read\",\n      \"url\": \"https://thelatentsource.com/articles/reactreel-media-agent\"\n    },\n    {\n      \"slug\": \"gulf-oil-spill-data-pipeline\",\n      \"title\": \"What a Moving Dot Can Hide: Building an Oil-Spill Data Explorer\",\n      \"summary\": \"I combined vessel records, satellite layers, and a timeline in a geospatial app. Then I checked which parts of the animation were observations and which were interpolation.\",\n      \"tags\": [],\n      \"published\": \"2026-09-21\",\n      \"reading_time\": \"4 min read\",\n      \"url\": \"https://thelatentsource.com/articles/gulf-oil-spill-data-pipeline\"\n    },\n    {\n      \"slug\": \"cdmx-transit-agent-mcp\",\n      \"title\": \"I Built a Transit Assistant That Has to Calculate the Route\",\n      \"summary\": \"A Gemini agent connects to MCP tools backed by PostGIS and pgRouting. The interesting part is resolving a place, choosing stations, and showing where the answer came from.\",\n      \"tags\": [],\n      \"published\": \"2026-09-21\",\n      \"reading_time\": \"3 min read\",\n      \"url\": \"https://thelatentsource.com/articles/cdmx-transit-agent-mcp\"\n    }\n  ],\n  \"total\": 3\n}"
            }
          ],
          "structuredContent": {
            "articles": [
              {
                "slug": "reactreel-media-agent",
                "title": "ReActReel: finding a movie scene or song from a remembered line",
                "summary": "A Gemini agent searches for movie scenes and songs, shows its tool calls, and returns a YouTube result inside the app.",
                "tags": [
                  "AI agents",
                  "ReAct",
                  "Gemini",
                  "Tool calling"
                ],
                "published": "2026-09-22",
                "reading_time": "2 min read",
                "url": "https://thelatentsource.com/articles/reactreel-media-agent"
              },
              {
                "slug": "gulf-oil-spill-data-pipeline",
                "title": "What a Moving Dot Can Hide: Building an Oil-Spill Data Explorer",
                "summary": "I combined vessel records, satellite layers, and a timeline in a geospatial app. Then I checked which parts of the animation were observations and which were interpolation.",
                "tags": [],
                "published": "2026-09-21",
                "reading_time": "4 min read",
                "url": "https://thelatentsource.com/articles/gulf-oil-spill-data-pipeline"
              },
              {
                "slug": "cdmx-transit-agent-mcp",
                "title": "I Built a Transit Assistant That Has to Calculate the Route",
                "summary": "A Gemini agent connects to MCP tools backed by PostGIS and pgRouting. The interesting part is resolving a place, choosing stations, and showing where the answer came from.",
                "tags": [],
                "published": "2026-09-21",
                "reading_time": "3 min read",
                "url": "https://thelatentsource.com/articles/cdmx-transit-agent-mcp"
              }
            ],
            "total": 3
          },
          "isError": false
        }
      },
      "status": 200,
      "elapsedMs": 315
    },
    {
      "request": {
        "jsonrpc": "2.0",
        "id": 4,
        "method": "tools/call",
        "params": {
          "name": "search_articles",
          "arguments": {
            "query": "MCP",
            "limit": 3,
            "lang": "en"
          }
        }
      },
      "response": {
        "jsonrpc": "2.0",
        "id": 4,
        "result": {
          "content": [
            {
              "type": "text",
              "text": "{\n  \"results\": [\n    {\n      \"slug\": \"mcp-servers-model-context-protocol\",\n      \"title\": \"MCP Servers: Building Tool-Using AI with Model Context Protocol\",\n      \"summary\": \"The Model Context Protocol standardizes how AI models discover and use tools. Here's how MCP servers work and why they matter for the agentic future.\",\n      \"tags\": [],\n      \"published\": \"2026-04-06\",\n      \"reading_time\": \"5 min read\",\n      \"score\": 0.03279,\n      \"url\": \"https://thelatentsource.com/articles/mcp-servers-model-context-protocol\"\n    },\n    {\n      \"slug\": \"turning-this-blog-into-an-mcp-server\",\n      \"title\": \"Turning This Blog Into an MCP Server\",\n      \"summary\": \"I exposed the blog’s retrieval system as three read-only MCP tools. The implementation is small enough to inspect, and the same search tool appears in recorded agent sessions.\",\n      \"tags\": [],\n      \"published\": \"2026-07-05\",\n      \"reading_time\": \"3 min read\",\n      \"score\": 0.03226,\n      \"url\": \"https://thelatentsource.com/articles/turning-this-blog-into-an-mcp-server\"\n    },\n    {\n      \"slug\": \"cdmx-transit-agent-mcp\",\n      \"title\": \"I Built a Transit Assistant That Has to Calculate the Route\",\n      \"summary\": \"A Gemini agent connects to MCP tools backed by PostGIS and pgRouting. The interesting part is resolving a place, choosing stations, and showing where the answer came from.\",\n      \"tags\": [],\n      \"published\": \"2026-09-21\",\n      \"reading_time\": \"3 min read\",\n      \"score\": 0.03126,\n      \"url\": \"https://thelatentsource.com/articles/cdmx-transit-agent-mcp\"\n    }\n  ],\n  \"total\": 3\n}"
            }
          ],
          "structuredContent": {
            "results": [
              {
                "slug": "mcp-servers-model-context-protocol",
                "title": "MCP Servers: Building Tool-Using AI with Model Context Protocol",
                "summary": "The Model Context Protocol standardizes how AI models discover and use tools. Here's how MCP servers work and why they matter for the agentic future.",
                "tags": [],
                "published": "2026-04-06",
                "reading_time": "5 min read",
                "score": 0.03279,
                "url": "https://thelatentsource.com/articles/mcp-servers-model-context-protocol"
              },
              {
                "slug": "turning-this-blog-into-an-mcp-server",
                "title": "Turning This Blog Into an MCP Server",
                "summary": "I exposed the blog’s retrieval system as three read-only MCP tools. The implementation is small enough to inspect, and the same search tool appears in recorded agent sessions.",
                "tags": [],
                "published": "2026-07-05",
                "reading_time": "3 min read",
                "score": 0.03226,
                "url": "https://thelatentsource.com/articles/turning-this-blog-into-an-mcp-server"
              },
              {
                "slug": "cdmx-transit-agent-mcp",
                "title": "I Built a Transit Assistant That Has to Calculate the Route",
                "summary": "A Gemini agent connects to MCP tools backed by PostGIS and pgRouting. The interesting part is resolving a place, choosing stations, and showing where the answer came from.",
                "tags": [],
                "published": "2026-09-21",
                "reading_time": "3 min read",
                "score": 0.03126,
                "url": "https://thelatentsource.com/articles/cdmx-transit-agent-mcp"
              }
            ],
            "total": 3
          },
          "isError": false
        }
      },
      "status": 200,
      "elapsedMs": 656
    },
    {
      "request": {
        "jsonrpc": "2.0",
        "id": 5,
        "method": "tools/call",
        "params": {
          "name": "get_article",
          "arguments": {
            "slug": "mcp-servers-model-context-protocol",
            "lang": "en"
          }
        }
      },
      "response": {
        "jsonrpc": "2.0",
        "id": 5,
        "result": {
          "content": [
            {
              "type": "text",
              "text": "# MCP Servers: Building Tool-Using AI with Model Context Protocol\n\n> The Model Context Protocol standardizes how AI models discover and use tools. Here's how MCP servers work and why they matter for the agentic future.\n\nPublished: 2026-04-06 · Source: https://thelatentsource.com/articles/mcp-servers-model-context-protocol\n\nEvery AI framework has its own way of defining tools. MCP is the attempt to make that a solved problem.\n\n## The problem MCP solves\n\nIf you've built tool-using agents, you've hit this wall. LangChain has one tool format. OpenAI has function calling with its own JSON schema. Anthropic has a different tool-use API. Every other framework invents its own convention. The tools themselves (search a database, call an API, read a file) are the same. The wiring is different every time.\n\nSo tool implementations aren't portable. A tool built for LangChain has to be rewritten for OpenAI's Assistants API, and rewritten again for whatever custom agent framework you're using this quarter. Multiply that by every tool in your stack and every model provider you want to support, and the integration surface becomes unmanageable.\n\nThe Model Context Protocol, introduced by Anthropic, standardizes the interface. It defines how AI models discover, describe, and invoke external tools, regardless of which model or framework is doing the calling.\n\n## Architecture\n\nMCP follows a client-server architecture:\n\n```mermaid\nflowchart TB\n    accTitle: MCP client and server communication\n    accDescr: A client discovers and invokes tools on an MCP server using JSON-RPC over stdio or HTTP. The server returns tool results.\n    M[\"MCP client\"]:::source\n    S[\"MCP server: tools\"]:::process\n    M -->|\"Discover and invoke\"| S\n    S -->|\"Tool results\"| M\n```\n\nAn MCP server is a process that exposes tools (and optionally resources and prompts) through a standardized interface. The server declares what tools it has, what parameters they take, and what they return.\n\nAn MCP client is the AI model's runtime, or the framework wrapping it, that connects to MCP servers, discovers available tools, and invokes them when the model decides to use one.\n\nThe transport is JSON-RPC, typically over stdio for local processes or HTTP with Server-Sent Events for remote servers. The protocol is transport-agnostic.\n\nThe point is separation of concerns. The tool implementer doesn't need to know which model will call their tool. The model doesn't need to know how the tool is implemented. MCP is the contract between them.\n\n## Building an MCP server\n\nAn MCP server in Python is short. Here's a minimal example that exposes a weather lookup tool:\n\n```python\nfrom mcp.server import Server\nfrom mcp.types import Tool, TextContent\nimport mcp.server.stdio\n\nserver = Server(\"weather-server\")\n\n@server.list_tools()\nasync def list_tools() -> list[Tool]:\n    return [\n        Tool(\n            name=\"get_weather\",\n            description=\"Get current weather for a city\",\n            inputSchema={\n                \"type\": \"object\",\n                \"properties\": {\n                    \"city\": {\n                        \"type\": \"string\",\n                        \"description\": \"City name, e.g. 'San Francisco'\"\n                    }\n                },\n                \"required\": [\"city\"]\n            }\n        )\n    ]\n\n@server.call_tool()\nasync def call_tool(name: str, arguments: dict) -> list[TextContent]:\n    if name == \"get_weather\":\n        city = arguments[\"city\"]\n        # Your actual weather API call here\n        weather = await fetch_weather(city)\n        return [TextContent(type=\"text\", text=f\"Weather in {city}: {weather}\")]\n    raise ValueError(f\"Unknown tool: {name}\")\n\nasync def main():\n    async with mcp.server.stdio.stdio_server() as (read, write):\n        await server.run(read, write, server.create_initialization_options())\n```\n\nThe TypeScript SDK follows the same pattern:\n\n```typescript\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nconst server = new Server({ name: \"weather-server\", version: \"1.0.0\" }, {\n  capabilities: { tools: {} }\n});\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n  tools: [{\n    name: \"get_weather\",\n    description: \"Get current weather for a city\",\n    inputSchema: {\n      type: \"object\",\n      properties: {\n        city: { type: \"string\", description: \"City name\" }\n      },\n      required: [\"city\"]\n    }\n  }]\n}));\n\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n  if (request.params.name === \"get_weather\") {\n    const weather = await fetchWeather(request.params.arguments.city);\n    return { content: [{ type: \"text\", text: weather }] };\n  }\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n```\n\n## Tool schemas\n\nMCP uses JSON Schema to describe tool inputs, which gives clients real type information for parameter validation and helps models figure out what arguments to provide.\n\nA well-designed tool schema needs clear descriptions on every property (the model reads these to decide how to use the tool), proper type constraints (string, number, array, enum values), required versus optional parameters so the model knows what it has to provide, and examples in the descriptions so the model can format arguments correctly.\n\n```json\n{\n  \"name\": \"search_database\",\n  \"description\": \"Search the product database with filters. Returns up to 20 results.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"query\": {\n        \"type\": \"string\",\n        \"description\": \"Natural language search query\"\n      },\n      \"category\": {\n        \"type\": \"string\",\n        \"enum\": [\"electronics\", \"clothing\", \"home\", \"books\"],\n        \"description\": \"Product category to filter by\"\n      },\n      \"max_price\": {\n        \"type\": \"number\",\n        \"description\": \"Maximum price in USD\"\n      },\n      \"in_stock\": {\n        \"type\": \"boolean\",\n        \"description\": \"If true, only return items currently in stock\",\n        \"default\": true\n      }\n    },\n    \"required\": [\"query\"]\n  }\n}\n```\n\nThe quality of your tool descriptions decides how well models use them. Vague descriptions produce wrong arguments. Be specific.\n\n## Beyond tools: resources and prompts\n\nMCP servers can expose more than tools.\n\nResources are read-only data sources the client can pull into context. Think of them as files, database records, or API responses the model can reference. A resource has a URI, a description, and content.\n\n```python\n@server.list_resources()\nasync def list_resources():\n    return [\n        Resource(\n            uri=\"docs://api-reference\",\n            name=\"API Reference\",\n            description=\"Complete API documentation\",\n            mimeType=\"text/markdown\"\n        )\n    ]\n```\n\nPrompts are reusable prompt templates the server provides. A client can list available prompts and render them with arguments. That lets tool providers ship optimized prompts alongside their tools.\n\nTools handle actions, resources carry context, prompts hold instructions. Together they give MCP servers a complete vocabulary for AI integration.\n\n## How it compares to OpenAI function calling\n\nOpenAI's function calling and MCP solve related but different problems.\n\nFunction calling is a model-level feature. You describe functions in the API request, and the model outputs structured JSON to call them. Execution happens on your side. It's a request-response pattern tied to one API call.\n\nMCP is a protocol-level standard. It defines how a runtime discovers and invokes tools across process boundaries. The model doesn't need to know it's using MCP. The client handles the protocol. MCP supports persistent connections, streaming, and server-initiated notifications.\n\nAnother way to think about it: function calling defines the format of a single tool call. MCP defines the whole lifecycle (discovery, invocation, streaming results, error handling) across multiple tools and servers.\n\nIn practice they're complementary. An MCP client might translate MCP tool schemas into OpenAI function calling format when it talks to GPT-4, and into Anthropic tool-use format when it talks to Claude. The MCP server doesn't care which model is on the other end.\n\n## Why this matters for multi-agent systems\n\nAgentic systems where multiple agents collaborate on complex tasks need a common tool interface more than single-agent systems do. Without a standard, every agent-to-tool connection is a custom integration. That's how integration backlogs swallow projects.\n\nMCP gives you tool marketplaces. Build a tool once, publish it as an MCP server, and any MCP-compatible agent can use it. The community already has MCP servers for databases, file systems, web browsing, GitHub, Slack, and dozens of other integrations.\n\nIt gives you dynamic tool discovery. An agent can connect to MCP servers at runtime and find out what tools are available, instead of having tools hardcoded at development time.\n\nIt gives you composability. An orchestrating agent can connect to several MCP servers at once (one for database access, one for email, one for code execution), and the model reasons across the union of available tools.\n\n## Practical deployment\n\nMCP servers are already supported in Claude Desktop (configure them in app settings), Claude Code (add them to your project configuration for code-aware tools), and IDE integrations like Cursor, Windsurf, and Cline. LangChain and LlamaIndex have framework integrations for existing agent pipelines.\n\nFor production deployment, the HTTP+SSE transport lets MCP servers run as remote services behind authentication. You deploy the server on your infrastructure, put it behind an API gateway, and let authorized clients connect to it.\n\nThe protocol is young and still evolving. But the trajectory is clear. If you're building tools that AI models should be able to use, MCP is the interface that gives your tool the most reach.\n"
            }
          ],
          "structuredContent": {
            "slug": "mcp-servers-model-context-protocol",
            "title": "MCP Servers: Building Tool-Using AI with Model Context Protocol",
            "summary": "The Model Context Protocol standardizes how AI models discover and use tools. Here's how MCP servers work and why they matter for the agentic future.",
            "tags": [],
            "published": "2026-04-06",
            "reading_time": "5 min read",
            "lang": "en",
            "url": "https://thelatentsource.com/articles/mcp-servers-model-context-protocol"
          },
          "isError": false
        }
      },
      "status": 200,
      "elapsedMs": 362
    },
    {
      "request": {
        "jsonrpc": "2.0",
        "id": 6,
        "method": "tools/call",
        "params": {
          "name": "get_article",
          "arguments": {
            "slug": "mcp-video-missing-article",
            "lang": "en"
          }
        }
      },
      "response": {
        "jsonrpc": "2.0",
        "id": 6,
        "result": {
          "content": [
            {
              "type": "text",
              "text": "No published article with slug 'mcp-video-missing-article'"
            }
          ],
          "isError": true
        }
      },
      "status": 200,
      "elapsedMs": 284
    }
  ]
}
