Skills, MCP, context engineering

MCP servers and clients

Run an MCP tool server

70 min4/6 in module

Learning objectives

  • Run an MCP tool server
  • Connect an agent client to it
  • Compare to ad-hoc tool registration

Why MCP exists: tools as a service boundary

Before the Model Context Protocol (MCP), every agent host reinvented tool wiring: custom JSON schemas in Python, ad-hoc HTTP wrappers, copy-pasted OAuth flows. MCP standardizes how clients (IDEs, chat hosts, agent runtimes) discover and invoke servers that expose tools, resources, and prompts over a defined transport (stdio, SSE, or streamable HTTP).

For AI engineers, MCP is not magic — it is RPC with a catalog. The win is separation of concerns: your billing team ships an MCP server; your agent team consumes it without importing billing's Python package into the agent repo.

Core primitives:

  • Tools — Model-invokable functions with JSON Schema inputs.
  • Resources — Readable data URIs (files, configs, live views).
  • Prompts — Templated prompt fragments the host can pull (less common in custom agents).

The agent loop you built earlier stays the same: model emits tool call → host executes → result returns as a message. MCP replaces how the host reaches the implementation.

Callout — Industry pattern: Platform teams expose internal capabilities as MCP servers behind corporate auth gateways — analogous to Uber-style agent platforms where identity and tool access are centralized, not embedded in each demo script.

Running an MCP tool server

Start with the smallest useful server: one read tool, one write tool (or write stubbed). Official SDKs exist for Python and TypeScript; the Python pattern is:

  1. Define tools with name, description, and inputSchema.
  2. Implement handlers that return structured TextContent or JSON.
  3. Run over stdio for local dev (simplest) or SSE when a remote host connects.

Example mental model for a get_weather tool:

  • Input schema: { "city": string, "units": "celsius" | "fahrenheit" }
  • Handler: call weather API, return { "temp": 22, "conditions": "cloudy" }
  • Errors: map HTTP 404 to { "error": "CITY_NOT_FOUND" } — never raw stack traces to the model.

Local dev checklist:

  • Server starts without agent attached (python server.py or npx @modelcontextprotocol/...).
  • list_tools returns expected schemas.
  • Handlers are unit-tested without an LLM in the loop.

Keep servers stateless where possible. Session state belongs in the agent host or a durable store, not hidden globals in the MCP process.

Connecting an agent client

The client side:

  1. Spawn or connect to the server (stdio pipe or URL).
  2. Call initialize handshake.
  3. Fetch tool list and map MCP tools to your host's tool registry (same names the model sees).
  4. On model tool call, dispatch to MCP call_tool and normalize the result into your message format.

Bridge code is thin but easy to get wrong:

  • Schema drift — MCP schema must match what you register with the model API.
  • Timeout alignment — MCP call timeout ≥ model tool timeout ≥ user-facing SLA.
  • Process lifecycle — stdio servers die when the agent exits; reconnect logic matters for long-running hosts.

Log every MCP invocation with server_id, tool_name, latency, and error class — same as first-party tools.

MCP vs ad-hoc tool registration

Concern Ad-hoc in-process MCP server
Deployment Same process as agent Separate process/service
Language Must match agent Any language with SDK
Versioning Git monorepo Server semver + contract tests
Auth Shared memory Gateway, mTLS, tokens per server
Testing Import and mock Contract tests + integration
Latency Lowest +IPC/network overhead

Choose ad-hoc when tools are trivial and co-located (calculator, string utils). Choose MCP when:

  • Multiple agents or products share the same capability.
  • The capability owner is a different team or repo.
  • You need sandboxing (crash the server, not the agent).
  • You will swap implementations (mock server in CI, real server in staging).

Stripe-style context hydration patterns often combine MCP resources (read current account state) with tools (mutate) so the model sees fresh data without giant prompt paste.

Security and auth (lab → production path)

Course labs use open local stdio. Production adds:

  • Identity propagation — Agent passes user/service token; MCP server validates scopes before handler runs.
  • Allowlists — Which agents may call which servers.
  • Audit — Immutable log of mutating tool calls with actor ID.

Your micro-project uses mock authz: a header or env var ACTOR_ROLE=support|admin that gates dangerous tools. That mirrors gateway checks without building full SSO.

Never put API keys inside MCP tool results returned to the model. Redact at the server boundary.

Debugging MCP integrations

Common failures:

  1. Empty tool list — Handshake incomplete; wrong transport; server stderr swallowed.
  2. Schema validation errors — Model emits extra fields; tighten additionalProperties: false in schema.
  3. Hung calls — Deadlock on stdio if server prints debug logs to stdout (always log to stderr).
  4. Stale server — Agent caches tool list at startup; restart after server deploy or implement refresh.

Keep a direct MCP client script (CLI or Inspector) to invoke tools without the LLM. When something breaks, bisect: server alone → client bridge → full agent.

Engineering problem (staff framing)

MCP standardizes tool/resource plumbing between hosts and servers — integration contract, not magic intelligence.

Diagram — MCP client/server

sequenceDiagram
  participant H as Host/agent
  participant C as MCP client
  participant S as MCP server
  H->>C: use tool
  C->>S: JSON-RPC
  S-->>C: result
  C-->>H: observation

Precise definitions & mental model

Servers expose tools/resources/prompts; clients mediate; auth boundaries.

Tradeoffs — when to use what

MCP portability vs custom tight integrations.

Failure modes (interview + on-call)

Overprivileged servers; trusting remote tool descriptions blindly.

Production & OSS practices

Sandbox servers; least privilege; contract tests.

Micro-project: MCP tool server + agent

In your portfolio:

  1. Implement an MCP server with ≥2 tools (one read, one mutating with mock authz).
  2. Connect your hand-rolled agent as MCP client; model must successfully call both tools in one session.
  3. Write MCP_vs_local.md comparing this setup to in-process tools from the agent module — latency, test story, ownership.
  4. Add one contract test: expected tool names and required schema fields.
  5. Capture a trace: model reasoning → MCP call_tool → normalized result.

Acceptance: stranger can run server + agent from README; mutating tool rejects unauthorized role.

Checklist

  • MCP server runs standalone and passes handler unit tests
  • Agent lists and invokes MCP tools end-to-end
  • Mock authz on mutating tool documented
  • Comparison doc: MCP vs ad-hoc registration
  • Contract test in CI or documented local command
Project checklist0/3 done

ShipAI delivery model is: