mcp-builder
Build high-quality MCP (Model Context Protocol) servers that let an AI interact with external services through well-designed tools.
Skill by tines
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/tines-mcp-builder
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: backend, security, productivity, agent-building
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/mcp-builder
What's inside
- Skill name
mcp-builder- Description
- Build high-quality MCP (Model Context Protocol) servers that let an AI interact with external services through well-designed tools. Use whenever you are building an MCP server to integrate an external API or service, in either Python (FastMCP) or TypeScript (MCP SDK). When the server is deployed as a 3B workflow, put it behind the built-in `space` or `tenant` route auth and let the platform authenticate clients instead of implementing the MCP OAuth handshake in the server.
- License
- Apache-2.0
- Compatibility
- Tines 3B
- Instructions
- 116 lines in SKILL.md · view on GitHub
Files (1)
- SKILL.md
SKILL.md (first lines)
# Building an MCP server
An MCP server exposes an external service to an AI as a set of tools. Its quality is measured by one thing: how reliably an AI can use those tools to accomplish real tasks. Build for the AI that will call it, not for human readers of the API.
## Phase 1 — Research and plan
- **Read the service’s API docs first.** Identify the key endpoints, the auth model, rate limits, and the core data shapes. Use web search and fetch the live docs rather than guessing.
- **Read the MCP spec and SDK docs.** Start from the sitemap at `https://modelcontextprotocol.io/sitemap.xml`, then fetch specific pages with a `.md` suffix. For the SDKs, fetch the TypeScript SDK README (`github.com/modelcontextprotocol/typescript-sdk`) or Python SDK README (`github.com/modelcontextprotocol/python-sdk`).
- **Decide coverage vs. workflow tools.** Comprehensive endpoint coverage gives the AI flexibility to compose operations; a few higher-level workflow tools are more convenient for common tasks. When unsure, prioritize comprehensive coverage and add workflow tools for the hot paths.
## Phase 2 — Implement
**Recommended stack:** TypeScript with the MCP SDK. Use streamable HTTP with stateless JSON for remote servers (simple to scale), and stdio for local servers.
Build shared infrastructure once: an authenticated API client, error-handling helpers, response formatting, and pagination.
For each tool:
- **Name it clearly and consistently.** Use an action-oriented, prefixed scheme — `github_create_issue`, `github_list_repos` — so the AI can find the right tool fast.
- **Define a strict input schema** with Zod (TypeScript) or Pydantic (Python). Add constraints, clear field descriptions, and examples in the descriptions.
- **Define an output schema** where possible and return structured content, not just text — it helps the client parse results.
- **Return focused data.** Support filtering and pagination so a single call can’t flood the context window.
- **Write actionable errors.** An error should tell the AI what went wrong and what to try next, not just echo a stack trace.
- **Set annotations** — `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint` — so clients can reason about safety.
## Deploying on 3B — let the platform authenticate clients
When the server runs as a 3B workflow (a step with a `route`, `route_type = "api"`), do **not** implement the MCP authorization spec — discovery, dynamic client registration, consent, or token exchange — inside the server. Set `route_auth = "space"` (or `"tenant"`) on the route: the `space` and `tenant` bars accept an OAuth access token as one of their credentials, so 3B handles the entire client-facing OAuth 2.1 handshake at the platform boundary — it serves the protected-resource and authorization-server metadata, registers clients, runs the consent screen, issues and verifies access tokens, and enforces them on every request.
```toml
# Routes are unique per space — use a /<server-name>/mcp path, never a bare "/mcp"
route = "/github/mcp"
route_type = "api"
route_auth = "space"
output = true
```
Routes are unique within a space, and publishing a second workflow on a path that’s already live is rejected — so give the route a `/<server-name>/mcp` path (`/github/mcp`, `/slack/mcp`) rather than a bare `/mcp`. MCP clients connect to the full URL you hand them, so the path is just a name; there’s no requirement to serve at `/mcp`.
Follow the `building-workflows` skill’s **Route URLs** guidance for links and callbacks generated by the workflow. Configure external MCP clients with the destination route’s URL after import.
Your step then receives only authenticated requests, with the consenting user’s email in the `x-3b-authenticated-email` header and the bearer **stripped** before it reaches your code — so never read, require, or forward the `Authorization` header for the server’s own auth. Each token is bound to this one route, independently revocable, and gated on the caller’s access to the workflow’s space.
To serve an **external audience** — partner organizations or customers whose users are not 3B members — use `route_auth = "sso"`. When the tenant has its own SSO provider registered in SSO settings with a pinned email domain (the built-in 3B sign-in doesn’t count), the platform runs the same client-facing OAuth handshake, but consent authenticates the human against that provider instead of a 3B account: callers hold a valid token vouched for by the tenant’s provider and on its pinned domain, with no 3B user, membership, or space access involved, and `x-3b-authenticated-email` carries the email the IdP vouched for.
Step code also receives verified group names in the spoof-proof `x-3b-authenticated-groups` header as a JSON array of strings. Parse it as JSON, never split on commas. It contains the IdP group snapshot for SSO callers and current 3B group names for members, and is absent when there are no verified groups. 3B strips caller-supplied values before injecting verified names.
To restrict an `sso` route to certain IdP groups, add `route_sso_groups = ["Security Team"]`; 3B requires membership in at least one listed group, compared case-insensitively, before the step runs. Other auth modes reject this setting. Draft previews require space access and do not enforce the SSO group restriction. The checks use the IdP groups captured at sign-in. That snapshot survives the fixed 30-day OAuth refresh chain, with up to 30 additional minutes for the final access token; refreshing does not pick up group changes. The IdP must emit the `groups` claim, and missing or oversized claims cannot satisfy a group restriction.
### Every request is a run, so refuse what you don’t serve
3B hands your step the raw HTTP request on stdin and turns every request into a workflow run, whatever the method (3B answers `OPTIONS` preflights itself, so your step never sees them). Streamable-HTTP clients open a `GET` on the endpoint to listen for server-to-client messages, and the spec requires you to either hold that stream open or answer `405 Method Not Allowed`. A `200` on `GET` tells the client the stream closed, so compliant clients reconnect immediately: one server that did this received a reconnect every two seconds from each connected Claude Code session, and each one was a full run. The one `GET` you must serve is the consent render described below, which arrives with `x-3b-consent-challenge`. After that check, answer `405` to anything but `POST` before parsing a body or touching a connector. With `method` and lowercased `headers` parsed from the request on stdin:
```ts
if (!headers["x-3b-consent-challenge"] && method !== "POST") {
const body = JSON.stringify({
jsonrpc: "2.0",
id: null,
error: {
code: -32000,
message: "Stateless MCP endpoint; send JSON-RPC over POST.",
},
});
process.stdout.write(
`HTTP/1.1 405 Method Not Allowed\r\nAllow: POST\r\nContent-Type: application/json\r\nContent-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`
);
} else {
// render consent when x-3b-consent-challenge is present, otherwise parse the JSON-RPC body
}
```
### Always render your own consent screen
The consent screen is the first thing a connecting user sees, so the workflow should own it — no config key, no second route. When a client signs in, 3B serves the OAuth route itself with a set of unspoofable request headers, and your step renders consent in response to them:
- `x-3b-consent-challenge` — an opaque signed token; echo it back untouched. It is the only thing with any authority.
- `x-3b-consent-decision-url` — where to send the decision; use it unchanged rather than constructing a workflow route URL.
- `x-3b-consent-client`, `x-3b-consent-email`, `x-3b-consent-scope` — display values for the copy.
- `x-3b-consent-connectors` — only when the route uses connectors in visitor mode, where each caller connects their own account: a JSON array of `{ id, name, connected, failed, connectUrl }`, one entry per connector, with `connectUrl` set while this caller still has to connect it.
When your step sees `x-3b-consent-challenge`, return an HTML page (set response header `x-3b-consent: rendered` so 3B serves it) showing the requesting client, the signed-in email, and Allow/Deny buttons in your own branding. Each button submits a plain form POST to the decision URL with fields `challenge` (the echoed token) and `decision` (`approve` or `deny`); 3B verifies it and finishes the OAuth flow, so the page needs no crypto, cookies, or state. When the header is absent, serve your normal response. A pure API server with no UI can simply not handle the header — 3B then shows a neutral built-in screen. When `x-3b-consent-connectors` is present, list each entry with its state and, while `connectUrl` is set, a plain link to it (“Connect”, or “Retry” when `failed`); the link usually goes straight to the provider and back to your consent page with the same challenge; connectors that sign in through a popup first show one 3B page with a Connect button. Keep Allow disabled until every entry is `connected` — 3B refuses an earlier approval by sending the browser back to your page.
HTML-escape the display values before rendering them: `x-3b-consent-client` is chosen by the connecting client when it registers and connector names are free text, so treat both as untrusted, and the page shares the tenant’s edge origin, so an unescaped injection is not confined to this workflow.
Challenges expire after ten minutes and 3B forces `Cache-Control: no-store` and denies framing on a consent render, so don’t fight those headers. Draft branches render consent the same way, so you can iterate before publishing.
This is the auth model for the MCP server _itself_. It is separate from how your tools authenticate to the external service they wrap (Phase 1) — that still uses the service’s own credentials.
### When the caller hasn’t connected their own account
When a tool calls a service through a connector in visitor mode, the connector uses the caller’s own account, which the consent screen normally collects before the first tool call. If the caller has no usable grant for the route’s space (they revoked it, it failed to refresh, or consent was skipped), 3B answers the tool’s outbound request itself with `403` and this JSON body instead of forwarding it:
```json
{
"error": "connector_not_connected",
"connector": "slack",
"message": "The caller hasn’t connected their own slack account for this space. Open connect_url in a browser to connect, then retry.",
"connect_url": "https://…/__3b/connectors/grant/connect?app=…&route=…&user=…"
}
```
Return `message` and `connect_url` verbatim as the tool result with `isError: true`, so the client shows the caller a link to connect and retry. Do not retry the request, treat it as a provider outage, or persist the link: it opens the caller’s connector list after they sign in, and is not a credential. A body without `connect_url` means the run had no caller identity to connect an account for, which happens on routes without authentication.
## Phase 3 — Review and test
Review for DRY code, consistent error handling, full type coverage, and clear tool descriptions. Build (`npm run build` / `python -m py_compile`), then exercise the server interactively with the MCP Inspector (`npx @modelcontextprotocol/inspector`).
## Phase 4 — Evaluate
Write ~10 evaluation questions that prove an AI can actually use the server. Each should be independent, read-only, realistic, and complex enough to need several tool calls, with a single verifiable answer that won’t drift over time. Solve each yourself first to confirm the answer. Run the questions against the server and fix the tools that make them hard.
---
_Adapted by 3B from Anthropic’s “mcp-builder” skill (github.com/anthropics/skills), © Anthropic PBC, licensed under Apache-2.0. Modified for 3B._From tines/skills, Apache-2.0.
More from tines/skills
- 0.592score
building-workflows
tines
Build and modify Tines 3B workflows from a workflow or space Git checkout, the 3B MCP server, the 3B CLI, or the in-product editor.
SkillAFreshness: A★ 1Claude Code +9 - 0.589score
docx
tines
Create, edit, and read Word documents (.docx) with python-docx.
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
pdf
tines
Read, extract from, create, fill, merge, split, and watermark PDF files with pdfplumber, pypdf, and ReportLab.
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
building-agents
tines
Build the AI parts of a 3B workflow — any step that calls a language model.
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
dashboard-design
tines
Build polished, interactive analytics dashboards for any data source.
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
tines-cases
tines
Working with Tines Cases from a 3B workflow or chat — creating, updating, listing, and managing cases with their comments, subscribers, and activities, plus…
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
tines-export-to-3b
tines
Your guide to reading a Tines story export JSON and rebuilding it as a 3B workflow.
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
claude-api
tines
Build, debug, and optimize code that calls the Claude (Anthropic) API.
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
skill-creator
tines
Write and improve 3B skills.
SkillAFreshness: A★ 1Claude Code +9 - 0.584score
pptx
tines
Create, edit, and read PowerPoint presentations (.pptx) with python-pptx.
SkillAFreshness: A★ 1Claude Code +9 - 0.584score
internal-comms
tines
Write internal communications — status reports, leadership updates, incident reports, project updates, FAQs, and newsletters — in clear, consistent formats.
SkillAFreshness: A★ 1Claude Code +9 - 0.584score
mcp-client
tines
Call tools on a remote MCP (Model Context Protocol) server over streamable HTTP.
SkillAFreshness: A★ 1Claude Code +9
Signals
- Stars
- 1
- Forks
- 2
- Open issues
- 0
- Last commit (this folder)
- Oct 22, 2026 (6 days ago)
- Last release
- Oct 23, 2026 (5 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- Apache-2.0
- Repository
- github.com/tines/skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from tines
- 0.592score
building-workflows
tines
Build and modify Tines 3B workflows from a workflow or space Git checkout, the 3B MCP server, the 3B CLI, or the in-product editor.
SkillAFreshness: A★ 1Claude Code +9 - 0.589score
docx
tines
Create, edit, and read Word documents (.docx) with python-docx.
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
dashboard-design
tines
Build polished, interactive analytics dashboards for any data source.
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
pdf
tines
Read, extract from, create, fill, merge, split, and watermark PDF files with pdfplumber, pypdf, and ReportLab.
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
skill-creator
tines
Write and improve 3B skills.
SkillAFreshness: A★ 1Claude Code +9 - 0.587score
tines-cases
tines
Working with Tines Cases from a 3B workflow or chat — creating, updating, listing, and managing cases with their comments, subscribers, and activities, plus…
SkillAFreshness: A★ 1Claude Code +9
Cite this page
mcp-builder skill. Agent Mart. https://agentmart.dev/skills/tines-mcp-builder (last updated 2026-10-24)