3gpp-mcp
3gpp-mcp
An MCP (Model Context Protocol) server that makes 3GPP specifications accessible to LLMs.
Background
3GPP specifications are essential references for mobile and telecommunications engineering, but they are difficult for LLMs to work with effectively:
Too many documents - Thousands of specifications exist across multiple series, making it hard to find the right one.
Individual documents are too large - Many specs are hundreds of pages long, far exceeding typical context windows.
Distributed as Word files - Specs are published in
.docx/.docformat and require conversion for text processing.Heavy cross-referencing - Specs frequently reference each other; reading a single document in isolation gives an incomplete picture.
Information packed in tables and figures - Complex tables and flow diagrams carry critical details. This tool converts tables to Markdown and extracts embedded images for LLM viewing.
Version complexity - The same specification exists across multiple 3GPP releases, and identifying the correct version matters.
This tool addresses these challenges by parsing the .docx files, structuring the content by section, and storing everything in a SQLite database with full-text search (FTS5). An MCP server then exposes tools for searching, browsing by section, and following cross-references — letting an LLM navigate the specifications the way an engineer would.
Why not RAG?
Embedding-based RAG is a common way to improve accuracy on document Q&A, and RAG systems specialized for 3GPP documents exist (Telco-RAG, TelcoAI). This tool takes a simpler approach: instead of building a retrieval pipeline in front of the model, it gives the model search and navigation tools and lets it explore the specifications the way an engineer would — full-text search, then following the section hierarchy and cross-references. Since retrieval is plain FTS5 search over structured sections, there is no embedding model or vector database to run, and everything lives in a single SQLite file.
Measured on TeleQnA, this lifts accuracy on 3GPP standards questions by 6.5 to 12.0 percentage points across three model families. Most of that is having the text at all: a single BM25 query over the same database accounts for +7.8 to +9.6pt of it. The tool's own search is what separates them on questions whose answer sits more than one hop from the first retrieved passage — on tasks generated from the specifications themselves (protocol codes, ASN.1 structure, 5G SBI schemas) it answers and correctly cites 88-100%, beating that same BM25 baseline by +26 to +88 points on every task type and every model. See BENCHMARK.md.
Related MCP server: mcp-docs
Getting Started
1. Install
# Homebrew
brew install higebu/tap/3gpp-mcp
# ...or with Go 1.26+
go install github.com/higebu/3gpp-mcp/cmd/3gpp-mcp@latestPrebuilt binaries are also available on the releases page. LibreOffice is optional (needed for .doc to .docx conversion and EMF/WMF image to PNG conversion).
2. Build the database
Download and import specifications into the database. Temporary files are deleted after each spec is processed, minimizing disk usage.
# Download and import the latest version of every spec (all releases)
3gpp-mcp build --latest --db data/3gpp.db --convert-doc --convert-image
# ...or restrict to a single release
3gpp-mcp build --release 19 --db data/3gpp.db --convert-doc --convert-imageThis will scrape the 3GPP FTP archive, download ZIP files, extract and parse .docx files, and insert structured content into the SQLite database.
3. Register with your MCP client
Claude Code
claude mcp add --scope user 3gpp -- 3gpp-mcp serve --db /path/to/data/3gpp.dbVS Code / GitHub Copilot
code --add-mcp '{"name":"3gpp","command":"3gpp-mcp","args":["serve","--db","/path/to/data/3gpp.db"]}'GitHub Copilot CLI
Add to ~/.config/github-copilot/cli-mcp.json (create if it doesn't exist):
{
"mcpServers": {
"3gpp": {
"command": "3gpp-mcp",
"args": ["serve", "--db", "/path/to/data/3gpp.db"]
}
}
}Codex CLI
codex mcp add --name 3gpp --command 3gpp-mcp --args serve --db /path/to/data/3gpp.dbClaude Desktop
Add to your configuration file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):
{
"mcpServers": {
"3gpp": {
"command": "3gpp-mcp",
"args": ["serve", "--db", "/path/to/data/3gpp.db"]
}
}
}4. Web viewer (optional)
Browse specifications in your browser by adding --web to the HTTP transport:
3gpp-mcp serve --db data/3gpp.db --transport http --addr :8080 --web
# MCP endpoint: http://localhost:8080/mcp/
# Web viewer: http://localhost:8080/Features: spec list with filtering, section viewer with TOC sidebar, full-text search with pagination, past-version browsing (versions are listed per spec and downloaded on demand, like the MCP tools), version comparison (structural summary and per-section diffs), embedded images, cross-reference links, OpenAPI definitions with syntax highlighting, KaTeX rendering of the LaTeX formulas the converter emits, dark mode, responsive design. Code blocks are syntax-highlighted per notation — ASN.1, Diameter, SIP/RTSP, SDP and XML (see Code blocks).
WebMCP
When the browser provides the W3C WebMCP API (document.modelContext, a Chrome origin trial as of 2026), the viewer registers all of its MCP tools with the browser at page load, so an in-browser agent can query the spec database directly. The registration is a thin same-origin passthrough to the /mcp/ endpoint — there is nothing to configure server-side, and browsers without the API are unaffected. During the origin trial, enable it locally via Chrome flags (chrome://flags), or for a shared deployment serve an Origin-Trial header from a fronting proxy.
Deployment
Streamable HTTP
The HTTP transport is stateless: it supports MCP protocol version 2026-07-28 (no
initialize handshake, no Mcp-Session-Id) while older clients (2024-11-05
through 2025-11-25) keep working through per-request sessions.
Start the server with HTTP transport:
3gpp-mcp serve --db data/3gpp.db --transport http --addr :8080Optionally enable Bearer token authentication:
export THREEGPP_MCP_BEARER_TOKEN=$(openssl rand -hex 32)
3gpp-mcp serve --db data/3gpp.db --transport http --addr :8080Then configure your client to connect via HTTP:
{
"mcpServers": {
"3gpp": {
"url": "http://your-server:8080",
"headers": {
"Authorization": "Bearer YOUR_SECRET_TOKEN"
}
}
}
}When using --web, the MCP endpoint moves to /mcp/.
See examples/systemd/ for production deployment with systemd.
Docker
The Dockerfile is multi-stage and builds the database for a release directly,
producing a self-contained image with the SQLite database (sections, OpenAPI
definitions, and embedded images) baked in. No pre-built database is needed in
the build context.
# Build an image with the latest version of every spec baked in (default)
docker build -t 3gpp-mcp:latest .
# ...or restrict the database to a single release
docker build --build-arg RELEASE=19 -t 3gpp-mcp:rel19 .
# ...or cap the newest release, keeping specs that have no version in it
docker build --build-arg MAX_RELEASE=19 -t 3gpp-mcp:max-rel19 .
# stdio transport (Claude Code / IDE integration)
docker run --rm -i 3gpp-mcp:latest
# HTTP transport
docker run --rm -p 8080:8080 3gpp-mcp:latest serve --db /3gpp.db --transport http --addr :8080RELEASE defaults to latest, which bakes in the latest version of every spec
across all releases. Set --build-arg RELEASE=<n> (e.g. 19) to restrict the
database to a single release, or --build-arg MAX_RELEASE=<n> to cap the newest
release without dropping specs that have no version in it. The two cannot be
combined.
Cloud Run
To run on Cloud Run, see cloudbuild.yaml (build + push + deploy) and
service.yaml (Cloud Run service spec).
Tools
Every tool below also has a CLI twin (list_specs → 3gpp-mcp list-specs, and
so on) for shell use and scripting — see the
query commands in the Command Reference.
Browsing specifications
Tool | Description | Key Parameters |
| List available specifications (paginated) |
|
| List the versions of a spec and where each can be read from |
|
| Get table of contents of a spec |
|
| Get section content (paginated) |
|
| Compare two versions of a spec: structural summary, or a section text diff |
|
Every get_toc, get_section and search result names the specification and
version it came from, on every page of a paginated response.
Past versions
The database holds one version per specification. To read another version, pass
version to get_section or get_toc. version accepts the dotted form
(15.8.0), the archive token (f80), a release selector (Rel-15 or 15,
picking the newest version in that release), or latest. Release selectors and
latest are resolved against the 3GPP archive, so they require on-demand
fetching (they do not work under --no-fetch). old_version and new_version
of compare_versions accept the same forms; new_version defaults to the
version in the database.
A version that is not in the database is downloaded from the 3GPP archive and
converted on first use. This takes up to a few minutes for a large
specification; if it is still running when the call's budget expires, the tool
says so and the same call repeated later returns the content. Results are kept
in a size-bounded cache (see serve) that is separate from the main
database, so:
searchcovers only the version in the database — cross-release full-text search is not supportedget_referencesonly has data for the version in the database, and a section read from an archived version says so in its headerget_imageandlist_imagesaccept aversiontoo: an archived version's images are downloaded on their own first use (one extra archive download per version, with the same retry behavior), and EMF/WMF figures are converted to PNG when LibreOffice is installed on the serversection numbers move between releases; check
get_tocfor the older version before reading a section of it
Searching
Tool | Description | Key Parameters |
| Full-text search across all specs |
|
The search tool supports SQLite FTS5 query syntax:
Phrase search:
"service based interface"Boolean operators:
AMF AND UE,AMF OR SMF,NOT deprecatedExclusion after a positive term:
handover -conditionalPrefix matching:
handov*Column filter:
title:authentication,content:handoverProximity:
NEAR(AMF UE, 5)
Terms containing hyphens or dots (IMS-AKA, 38.101) are quoted automatically,
so they need no manual escaping.
Cross-references
Tool | Description | Key Parameters |
| Get cross-references between specs and RFCs |
|
OpenAPI definitions
Tool | Description | Key Parameters |
| List available OpenAPI definitions |
|
| Get OpenAPI definition (paginated) |
|
| Full-text search across OpenAPI definitions |
|
search_openapi uses its own FTS5 index, separate from the one search uses:
search covers specification clause text and never returns OpenAPI content,
search_openapi covers OpenAPI content only. One hit is one definition rather
than one document — a schema from components.schemas, or one HTTP method of
one path (named like PUT /nf-instances/{nfInstanceID}) — so you can find a
data type or an endpoint without knowing which API document defines it, then
read it in full with get_openapi. A query that is a single bare term ranks a
definition of exactly that name first, so NFProfile returns the NFProfile
schema ahead of the schemas that only reference it.
A schema's indexed text carries one level of $ref expansion — through items
and additionalProperties as well as directly, which is how the 5G SBI
definitions state most of their relationships — so the fields of a referenced
type are searchable from the schema that uses it; a type two hops away is not
in that text. Unlike search, this index applies no stemming
— identifiers are matched as written — and -, . and _ split tokens, so
Nnrf_NFManagement is also found by NFManagement and /nf-instances by
instances. camelCase is not split.
The index is built at the end of build and update. import and import-dir
leave it alone: the YAML files ship in the archive zip, so importing a .docx
cannot change what there is to index. A database built before this tool existed
has no index; add it in place with
build-openapi-index.
ASN.1 definitions
Tool | Description | Key Parameters |
| Get an ASN.1 assignment by name — in one spec or across all of them — or list a spec's assignment names |
|
The ASN.1-specified protocols (RRC TS 38.331/36.331, NGAP TS 38.413, S1AP
TS 36.413, XnAP, F1AP, ...) write their ASN.1 between -- ASN1START /
-- ASN1STOP markers, which the converter stores as ```asn1 fences (see
Code blocks). get_asn1 extracts every top-level assignment —
types, constants and information objects — from those fences.
With name it returns that assignment's full text together with the section
that defines it, so the answer can be cited. This matters for the protocols
that define all their IEs in one clause: NGAP's IE definitions clause is
hundreds of kilobytes, far more than one get_section page, while the one
definition that answers "what range does the ASN.1 allow here" is a few lines.
Matching ignores case and separators, so the IE table's AMF UE NGAP ID finds
the ASN.1's AMF-UE-NGAP-ID; a name that matches nothing gets similar names
suggested. A name defined more than once returns every definition, each under
its own source line.
When you do not know which specification defines a name, omit spec_id: the
name is resolved across every specification in the database, from a name
index built at database build time (build, update, import and
import-dir all refresh it). A lookup that names the wrong specification
gets told where the name is actually defined. A database built before this
tool existed has no index — add it in place with
build-asn1-index. Cross-spec resolution covers the
database versions only — pass spec_id (and optionally version) to read
an archived version, with the same on-demand download behavior as
get_section.
With a spec_id and no name it lists every assignment name, grouped by
defining section.
Embedded images
Tool | Description | Key Parameters |
| List embedded images in a spec |
|
| Get an embedded image as base64 data viewable by LLMs |
|
PNG/JPEG/GIF/WebP images are directly viewable by LLMs. EMF/WMF images (most 3GPP figures use this format) are stored as raw data by default; use --convert-image to convert them to PNG via LibreOffice at build time.
Figures are referenced from the section text in a single notation, whatever the
image format:  in body text and
<img src="image://NAME?w=&h=" ...> inside table cells. Pass that NAME to
get_image; both the original filename (image3.emf) and the converted one
(image3.png) resolve.
Code blocks
Section text carries tagged code fences, so both LLMs and the web viewer can tell the notations apart:
Fence | Content |
| ASN.1 modules between the |
| Diameter command and grouped-AVP definitions (RFC 6733 CCF) |
| XML schemas, XML body examples and DTDs |
| SIP/RTSP message examples |
| Standalone SDP session descriptions |
| Standalone equations converted from Word OMML |
| Anything else the source document styles as code |
Formulas
Word formulas (OMML) are converted to LaTeX in three notations, so a formula is readable whether it stands alone or sits in a sentence:
Notation | Where |
| A paragraph whose only content is an equation. Its equation number is kept as |
| Display equations that cannot be a fenced block — inside a table cell or a list item. |
| A formula inside a sentence. |
Indentation
3GPP prose encodes structure in indentation — nested requirement and
condition lists, multi-level definitions. A body paragraph's leading
whitespace is preserved as no-break spaces (U+00A0), one tab of the source
document becoming four: a literal tab or 4+ leading spaces would turn the
line into an indented code block in Markdown (inside which HTML like
<sub> is never interpreted), while no-break spaces keep the visual
nesting in any renderer and stay out of the way of full-text search.
Tips
Tell the model to use the tools
Attaching the server does not by itself make a model consult it: given the choice, some models answer 3GPP questions from memory. In the benchmark, Claude Sonnet 5 skipped retrieval on 40% of TeleQnA questions and GPT 5.6 Luna on 60%, and on those questions the tools were worth nothing. One sentence in the client's system prompt removes that discretion. The measured wording:
Do not answer from memory. Search the specifications first and base your answer on the text you retrieve, even when you are confident you already know the answer.
That sentence took Luna's skip rate to zero and its gain from +5.9 to +12.0 points, moved nothing on a model that already searched every question, and is worth nothing without the tools attached — it forces retrieval rather than smuggling in an answer. Stronger house rules in the same spirit — base every answer about 3GPP on clause text retrieved through these tools, and cite the clause — are reasonable, but only the sentence above is what the benchmark measured.
Separate databases per release
For spot comparisons across releases, compare_versions and the version parameter need no extra setup. Building a separate database per release still pays off when you work against one release continuously: full-text search, get_references and OpenAPI definitions only cover the version baked into the database, so a release-specific database gives you all three for that release, with no on-demand downloads.
# Build databases for different releases
3gpp-mcp build --release 18 --db data/3gpp-rel18.db --convert-doc --convert-image
3gpp-mcp build --release 19 --db data/3gpp-rel19.db --convert-doc --convert-image--release keeps only specs that have a version in that exact release, so a
spec frozen in an earlier release (TS 34.108, for example) is missing from the
database entirely. To pin a release without losing those specs, cap the
selection instead — every spec is taken at its newest version at or below the
cap:
# Everything as of Release 19: specs with no Rel-19 version fall back to their
# newest older version rather than dropping out.
3gpp-mcp build --max-release 19 --db data/3gpp-rel19.db --convert-doc --convert-image
# Keep the cap when refreshing the database later.
3gpp-mcp update --max-release 19 --db data/3gpp-rel19.db --convert-docRegister them as separate MCP servers:
claude mcp add --scope user 3gpp-rel18 -- 3gpp-mcp serve --db /path/to/data/3gpp-rel18.db
claude mcp add --scope user 3gpp-rel19 -- 3gpp-mcp serve --db /path/to/data/3gpp-rel19.dbKeeping specs up to date
Use the update command to check for newer versions of specs already in your database:
3gpp-mcp update --db data/3gpp.db --convert-doc --convert-imageCommand Reference
serve
Start the MCP server.
Flag | Description | Default |
| Path to SQLite database |
|
| Transport type: |
|
| HTTP listen address (env: |
|
| Bearer token for HTTP auth (env: | |
| Enable web viewer alongside MCP server (HTTP transport only) |
|
| Disable on-demand fetching of spec versions that are not in the database |
|
| Path to the on-demand version cache |
|
| Size limit of the version cache in MB. |
|
| How long a tool call waits for an on-demand fetch before asking the caller to retry (env: |
|
The version cache is a separate SQLite file, so the main database stays
read-only and is never polluted with extra versions. When the cache cannot be
created — a read-only or ephemeral filesystem, such as the scratch-based
container image — the server logs a warning and runs with on-demand fetching
disabled; everything else keeps working. Cached versions are evicted
least-recently-used once the size limit is exceeded.
HTTP transport also exposes GET /health, which returns 200 OK without authentication. Use this path for platform health checks (Cloud Run, Sakura AppRun, Kubernetes liveness/readiness probes, etc.).
build
Download and import specifications into the database (recommended for initial setup). Alias: pipeline.
Flag | Description | Default |
| Output SQLite database path |
|
| Process specs for a specific release (e.g. | |
| Cap the selection at a release (e.g. | |
| Select every spec at its latest version (use when no other selector is given) |
|
| Process a specific spec (e.g. | |
| Filter by series, comma-separated (e.g. | |
| Number of parallel workers | NumCPU |
| Convert |
|
| Convert EMF/WMF images to PNG using LibreOffice |
|
| Read the spec list from a file instead of scraping the archive (a selector is still required) | |
| Disable the spec list cache |
|
| Concurrency for scraping spec listings ( |
|
| HTTP timeout |
|
One of --release, --max-release, --latest, --series or --spec must be
given, --spec-list included: the file supplies the candidate entries and the
selector filters them.
--release and --max-release differ in what happens to a spec that has no
version in the named release: --release 19 drops it, --max-release 19 keeps
it at its newest version below the cap. They cannot be combined.
Other commands
download— Download specifications without conversion (--output-dir, defaultspecs). Requires one of--release,--max-release,--latest,--seriesor--spec, likebuild.import— Import a single.docxfile into the database. Alias:convert. Usage:3gpp-mcp import --db data/3gpp.db path/to/spec.docximport-dir— Import all.docxfiles in a directory into the database. Alias:convert-dir. Usage:3gpp-mcp import-dir --db data/3gpp.db ./specsupdate— Update specifications in the database to latest versions, or to a cap with--max-release.build-openapi-index— Rebuild the OpenAPI search index of an existing database.buildandupdatedo this themselves, so it is for adding the index to a database built beforesearch_openapiexisted:serveopens the database read-only and cannot create it on the fly.build-asn1-index— Rebuild the ASN.1 name index of an existing database.build,update,importandimport-dirdo this themselves, so it is for adding the index to a database built beforeget_asn1existed.completion— Print a shell completion script:3gpp-mcp completion bash(orzsh,fish)
The cap is not stored in the database, so a database built with
--max-release 19 needs the same flag on update — otherwise the update
lifts every spec to the newest release on the archive. With a cap the update moves a spec
in either direction, so it also brings an already-built uncapped database down
to the cap; a spec whose every version is above the cap is removed, since no
version of it belongs in a capped database. A spec missing from the archive
listing is left untouched, as a failed listing looks the same as a withdrawn
spec.
Query commands
The query commands (list-specs, list-versions, get-toc, get-section,
get-asn1, compare-versions, search, list-openapi, get-openapi,
search-openapi, get-references, list-images, get-image) mirror the MCP
read tools 1:1, so
the database can be inspected and scripted from a shell without an MCP client:
3gpp-mcp search --db data/3gpp.db --limit 3 "AMF AND authentication" | jq '.results[].section_number'
3gpp-mcp get-section --db data/3gpp.db "TS 23.501" 5.15.2 | lessConventions shared by all of them:
Flags must come before positional arguments.
JSON results print to stdout indented and unpaginated — pipe to
jq,headorless. Warnings and progress notes go to stderr, so stdout stays parseable.Commands that accept
--version(andcompare-versions) take the same version forms as the MCP tools (15.8.0,f80,Rel-15,latest) and wait for an on-demand download to finish instead of asking you to retry; interrupt with Ctrl-C. They shareserve's fetch flags:--no-fetch,--version-cache,--version-cache-mb,--fetch-budget. Queries that name no version never create the version cache (list-versionsreads an existing cache to reportcachedavailability, but will not create one).Every command takes
--db(default3gpp.db).
Environment Variables
Variable | Description |
| Transport for |
| HTTP listen address for |
| Bearer token for HTTP transport auth |
| PaaS convention (Cloud Run / Heroku); |
| Size limit of the on-demand version cache in MB (default |
| How long a tool call waits for an on-demand fetch (default |
| Max ZIP download size (default |
| Spec list cache TTL in hours (default |
| Initial backoff between archive listing fetch attempts in ms (default |
| Cache directory root, per the XDG Base Directory spec |
Maintenance
Related MCP Servers
- AlicenseAqualityFmaintenanceEnables AI assistants to access and search 3GPP telecommunications specifications through direct integration with the TSpec-LLM dataset. Provides real-time specification content, implementation requirements, and multi-spec comparisons for 3GPP standards development.43129MIT
- Alicense-qualityDmaintenanceGeneric MCP server that exposes Markdown documentation to LLMs, enabling them to search and answer questions about any software documentation.MIT
- Alicense-qualityDmaintenanceAn MCP server that indexes documents and serves relevant context to LLMs via Retrieval Augmented Generation (RAG).24536MIT
- AlicenseAqualityBmaintenanceA local-first MCP server that ingests PDFs, extracts structure, and provides semantic search and sequential navigation tools for AI clients to query and learn from documents.10MIT
Related MCP Connectors
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
MCP server for AgentDocs (agentdocs.eu): read, search, write, comment on & share Markdown docs.
MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/higebu/3gpp-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server