Custom MCP servers
A server that exposes your database, warehouse, or internal service as a set of typed, scoped, audited tools — deployed on your infrastructure, in your language.
LLM.coPrivate, self-hosted LLM deployments
Legal AI infrastructure for firms
AI RFP discovery and response drafting
Automatic.coBusiness process automation
Secure AI virtual data roomsMCP & WebMCP Development
We build the Model Context Protocol servers, gateways, and WebMCP browser tools that let agents use your data and your product — with scoped authorization, audit logging, and evals, not a blanket API key and hope.
Built to the 2026-07-28 spec · TypeScript · Python · Go · C# · You own the code
The integration problem
For two years the answer to “can the AI see our data?” was a bespoke integration: a function-calling schema here, a plugin manifest there, a scraper held together with retries. Every new model meant rewriting the same connectors, and every new system meant doing it again for each model.
The Model Context Protocol is the standard that collapses that grid. You build one MCP server per system; any compliant host — Claude, ChatGPT, an IDE, a coding agent, or an application you wrote yourself — discovers its tools and calls them over the same wire format. N × M becomes N + M, which is the same trade the industry made with ODBC for databases and the Language Server Protocol for editors.
A server exposes three things: tools the agent can invoke, resources it can read, and prompts the host can offer a user. What it does not expose is your credentials. The server is the only component that holds them, and it decides — per call, per identity — what actually runs.
Current as of the 2026-07-28 revision
The July 2026 revision is the largest change to MCP since authorization was introduced, and it is mostly good news for anyone who has to run this in production.
Protocol-level sessions are gone, along with the Mcp-Session-Id header. Every request carries its own protocol version, client identity, and capabilities, so any instance behind an ordinary load balancer can answer any request. No sticky sessions, no session store, no bespoke infrastructure.
Method and tool names now travel in the Mcp-Method and Mcp-Name headers, so a gateway can route and authorize a call without parsing the JSON body — which is what makes a central policy layer practical rather than theoretical.
Multi round-trip requests let a server come back mid-call for a missing parameter or an explicit human confirmation, without holding a persistent bidirectional stream open. This is the mechanism we use to gate anything destructive.
Roots, Sampling, and Logging are deprecated alongside the legacy HTTP+SSE transport, each with a minimum twelve-month support window. Authorization moves to Client ID Metadata Documents and mandatory RFC 9207 issuer validation. If your server predates this, it still runs — but it is now on a clock.
// orders-server.ts — one tool, wired the way production needs it
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
const server = new McpServer({ name: "acme-orders", version: "1.4" });
server.registerTool("refund_order", {
title: "Refund an order",
description: "Issues a full or partial refund on a paid order.",
inputSchema: {
orderId: z.string(),
amountCents: z.number().int().positive(),
},
annotations: { readOnlyHint: false, destructiveHint: true },
}, async ({ orderId, amountCents }, { authInfo }) => {
// a scope for this tool — never one blanket key per server
requireScope(authInfo, "orders:refund");
const order = await orders.get(orderId);
if (order.status !== "paid") throw new ToolError("not refundable");
const receipt = await billing.refund(order, amountCents, {
idempotencyKey: order.id,
});
audit.write({ actor: authInfo.sub, tool: "refund_order", receipt });
const text = JSON.stringify(receipt);
return { content: [{ type: "text", text }] };
});Nine lines of that snippet are the interesting ones: the scope check, the state precondition, the idempotency key, and the audit write. The tool definition itself is the easy part — the reason MCP projects go wrong is everything around it.
WebMCP
WebMCP moves the same idea into the browser. Instead of standing up a server with its own credentials, the page registers its own actions on document.modelContext, and an agent running in the browser can list them, call them, and read structured results back.
The consequence is the interesting bit: the agent operates inside the user's existing session. It inherits their cookies, their SSO, and their role — so it can do exactly what that person could do by clicking, and nothing more. There is no new credential to issue, no new blast radius to model, and no integration contract to negotiate with whoever owns the backend.
Be clear-eyed about maturity, though. WebMCP is a Draft Community Group Report from the W3C Web Machine Learning Community Group — not a W3C Standard and not on the Standards Track. Chrome has shipped it and run origin trials, Microsoft co-authors it, and Firefox and Safari are engaged without committed timelines. The API has already moved once, from navigator.modelContext to document.modelContext. We build it behind a thin feature-detected adapter so that when the spec moves again, you change one file.
// Published by the page itself. No backend, no API key.
document.modelContext.registerTool({
name: "filter_invoices",
description: "Filters the invoice table by status and date.",
inputSchema: {
type: "object",
properties: {
status: { type: "string", enum: ["paid", "open", "overdue"] },
since: { type: "string", format: "date" },
},
required: ["status"],
},
annotations: { readOnlyHint: true, untrustedContentHint: true },
async execute({ status, since }, { signal }) {
const rows = await applyTableFilters({ status, since }, signal);
const text = rows.length + " invoices matched";
return { content: [{ type: "text", text }] };
},
});The agent no longer has to guess at your DOM, invent selectors, or drive the page by simulated clicks. It gets a named action with a typed schema and a structured result — the same contract an MCP server would offer, published by the page.
readOnlyHint tells the agent this tool changes nothing, so a host can run it without a confirmation prompt. untrustedContentHint does the opposite job — it marks the returned rows as third-party text that should never be read as instructions.
And because the tool executes in the page, it can only touch what the signed-in human could already touch. Authorization is a problem you already solved.
| MCP server | WebMCP tool layer | |
|---|---|---|
| Where it runs | On your infrastructure | In the page, in the browser |
| Authorization | OAuth 2.1, scoped per tool | Inherits the user's session |
| Who can reach it | Any MCP host, anywhere | Whoever is on the page |
| Best for | Data, back office, SaaS APIs | Existing web products |
| Maturity | Stable spec, four Tier 1 SDKs | Community Group draft, Chrome-first |
| Typical effort | 3–6 weeks | 1–3 weeks |
Engagements
Six shapes of work, all of them ending with running software in your repository and on your infrastructure.
A server that exposes your database, warehouse, or internal service as a set of typed, scoped, audited tools — deployed on your infrastructure, in your language.
Ship MCP as a product surface so your customers' agents can drive your app. Multi-tenant auth, per-plan tool exposure, rate limiting, and usage metering included.
Register your web app's real actions on document.modelContext so a browser agent can operate the product inside the user's own session — no integration contract required.
One authenticated front door for many servers: header-based routing, central policy, a private registry, and the observability to see which tool is actually being called.
The other half of the protocol — agents, copilots, and internal chat surfaces that discover tools, negotiate permissions, and hold a coherent conversation across them.
Audit an existing server against the 2026-07-28 revision, retire the deprecated HTTP+SSE transport, move off Dynamic Client Registration, and close the injection paths.
How we work
We start from the jobs the agent has to finish, then work backwards to the smallest set of tools that finishes them. A model given forty vaguely-named tools performs worse than one given six well-named ones, so the inventory — names, descriptions, schemas, and the boundary of each call — is a design deliverable, not an afterthought.
Every tool gets its own scope, and the server never holds a credential broader than the tools it exposes. We map how identity travels from the human, through the host and the client, to your systems — and where a human confirmation has to interrupt the call.
Stateless request/response core, Mcp-Method and Mcp-Name header routing, cacheable tool lists with ttlMs and cacheScope, and multi round-trip requests where a tool genuinely needs to come back and ask. Written on the Tier 1 SDKs — TypeScript, Python, Go, or C#.
We build a graded task set the way we build a RAG eval harness: does the agent pick the right tool, pass valid arguments, recover from an error, and refuse when it should? Then we attack it — injected instructions in tool output, argument smuggling, and scope escalation.
Deployed to your infrastructure behind ordinary HTTP load balancers, with structured audit logs, per-tool latency and error rates, and spend caps. You get the repo, the runbook, and thirty days of post-launch support.
Security
Giving an agent tools converts a language problem into an authorization problem. The dangerous deployments are not the sophisticated ones — they are the ones where a single broad API key sits behind a hundred cheerfully-named tools.
A support ticket, a web page, or a PDF returned to the model can contain instructions aimed at the model. Tools that surface third-party content get flagged with untrustedContentHint, and privileged tools never sit one hop away from unvetted text.
The server does not accept a token minted for someone else and replay it downstream. Authorization is verified per request, with RFC 9207 issuer validation, and the newer Client ID Metadata Documents flow instead of Dynamic Client Registration.
read_customer and delete_customer do not share a credential. Scopes are declared per tool and enforced server-side, so a jailbroken prompt cannot widen its own reach.
Multi round-trip requests let a tool pause mid-call to collect an explicit human approval, so destructive actions are not the outcome of a single confident-sounding sentence.
Who called which tool, with which arguments, under which identity, and what changed. When someone asks what the agent did last Tuesday, that is a query, not an investigation.
The one that catches people
Indirect prompt injection. A ticket body, a scraped page, or a PDF your tool returns can carry instructions written for the model rather than the human — and a model with a delete_customer tool one hop away will sometimes take them. We test for exactly this before launch, and we keep privileged tools out of reach of unvetted text.
Research
Source-grounded guides to the MCP ecosystem — what each project connects, how it is licensed, and what it takes to run it in production.
FAQ
MCP is an open protocol that standardises how AI applications connect to external tools and data. Instead of writing a bespoke integration for every model-and-system pair, you build one MCP server per system and any compliant host — Claude, ChatGPT, an IDE, or your own agent — can discover and call it. It turns an N × M integration problem into an N + M one, which is the same reason people standardised on ODBC, LSP, and OpenAPI before it.
WebMCP takes the same idea and moves it into the browser. Rather than running a server that an agent connects to over the network, your web page registers tools directly on document.modelContext, and an agent running in the browser can list and call them inside the user's existing session.
The practical difference is authentication and integration cost. An MCP server needs its own credentials and its own deployment. A WebMCP tool inherits the cookies, SSO session, and permissions the human already has on the page — so the agent can only ever do what that person could do by clicking. It is the fastest path to making an existing web app agent-operable.
Not yet, and we are direct with clients about that. WebMCP is a Draft Community Group Report from the W3C Web Machine Learning Community Group — explicitly not a W3C Standard and not on the Standards Track. Chrome shipped an implementation and has been running origin trials; Microsoft co-authors the spec; Firefox and Safari are engaged but have not committed to timelines.
The API surface has already moved once — from navigator.modelContext to document.modelContext — which is exactly what you would expect from a spec at this stage. We therefore build WebMCP behind a thin adapter with feature detection and a graceful no-op fallback, so a spec change is a small, contained edit rather than a rewrite.
2026-07-28, the current revision. It is the most substantial change to the protocol since authorization was added: the transport became a stateless request/response protocol with no protocol-level sessions and no Mcp-Session-Id, so any instance behind an ordinary load balancer can answer any request.
It also added multi round-trip requests for mid-call confirmations, moved method and tool names into Mcp-Method and Mcp-Name headers so gateways can route and authorize without parsing JSON bodies, made list results cacheable with ttlMs and cacheScope, and formalised Tasks, MCP Apps, and Enterprise Managed Authorization as extensions rather than core features.
Mostly transports and authorization. The legacy HTTP+SSE transport is deprecated, as are Roots, Sampling, and Logging — each with a minimum twelve-month support window, so you have time but not indefinite time. On the auth side, Dynamic Client Registration gives way to Client ID Metadata Documents, and RFC 9207 issuer validation becomes a requirement rather than a good idea.
Our migration engagement is a fixed-scope audit: we run your server against the current spec and the Tier 1 SDKs, produce a prioritised diff, and then do the work. Most servers are a week or two of engineering; the interesting findings are usually in authorization, not transport.
It is safe when the MCP server is treated as a policy layer rather than a passthrough. The dangerous pattern is handing a model one broad API key and a hundred tools. The workable pattern is per-tool scopes, verified identity on every request, human confirmation on destructive or costly actions, hard rate and spend caps, and a structured audit log for everything.
The failure mode people underestimate is indirect prompt injection: instructions hidden in content your tools return — a ticket body, a scraped page, a document — that the model then follows. We handle that with the untrustedContentHint and readOnlyHint annotations, by keeping privileged tools away from unvetted text, and by adversarially testing the deployment before it goes live.
A focused first server — one system, a handful of tools, real authorization, deployed and observable — is typically three to six weeks. A gateway serving several internal servers with a shared policy layer and registry runs longer. WebMCP tool layers on an existing web app are usually faster, because there is no new backend to stand up.
We scope fixed-fee where the boundary is clear and run a discovery sprint first where it is not. Start with the software cost calculator for a rough envelope, then talk to us for a real number.
The Tier 1 MCP SDKs — TypeScript, Python, Go, and C# — plus Rust where a team is already there. In practice most of our servers are TypeScript or Python, deployed as ordinary containers on whatever you already run: AWS, GCP, Azure, Fly, Cloudflare, or your own Kubernetes. Because the 2026-07-28 core is stateless, no special session-affinity infrastructure is required.
Yes, fully, and it lives in your repository from the first commit rather than being handed over at the end. You get the source, the infrastructure definitions, the eval suite, the runbook, and thirty days of post-launch support. We are frequently retained afterwards to extend the tool surface, but nothing about the engagement depends on that.
If the value is in your data and back-office systems, and the consumers are AI hosts your team or your customers already use, build the MCP server. If the value is in your web product and you want an agent to be able to operate it the way a person does, start with WebMCP — it is cheaper, requires no credential design, and works within permissions you have already modelled.
Plenty of teams end up wanting both, and they compose cleanly: the same tool inventory and the same authorization model can back a network-facing server and an in-page tool layer.
Related services
Tools are how an agent acts. Retrieval is how it knows things. Most engagements touch both — here is the rest of the practice.
Bring us a system you want an agent to use and we will come back with a tool inventory, an authorization model, and a fixed-scope plan to get it into production.