package.json Configuration
Configure your App using standard package.json fields. No custom config files needed.
Good news -- if you've ever published an npm package, you already know most of this.
mctx reads your standard package.json for everything it needs. No custom config files, no proprietary formats. You set metadata in package.json, instructions in code, and capabilities are detected automatically when you deploy.
A Working package.json
Here is a complete, deploy-ready package.json:
{
"name": "weather-api",
"version": "1.0.0",
"description": "Get real-time weather data for any location worldwide",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "esbuild src/index.js --bundle --minify --platform=node --format=esm --outfile=dist/index.js"
},
"dependencies": {
"@mctx-ai/app": "^0.3.0"
},
"devDependencies": {
"esbuild": "latest"
}
}
That is the entire configuration. Let's walk through what each field does and why you'd set it.
Required Fields
name
Your App's display name. This shows up on your App info page and in the MCP Community Registry.
{
"name": "weather-api"
}Follow standard npm naming conventions -- lowercase, no spaces, hyphens are fine.
The name field is your App's display name only. Your App's URL slug (the subdomain used to
route requests, e.g., weather-api.mctx.ai) is set separately when you create the App in the
dashboard. The slug does not have to match the name field.
version
The version number that triggers deployments and gives subscribers stable endpoints. mctx uses semantic versioning:
{
"version": "1.0.0"
}Bump this number and push. mctx detects the change and deploys automatically. No version bump, no deployment -- even if your code changed.
See Versioning for the full picture on how versions, URLs, and deployments work together.
description
Think of this field as marketing copy, not a technical field. It is the pitch. The description is the first thing a potential subscriber reads after your App name — on the public App info page, in the MCP Community Registry listing, and in the platform-generated llms.txt that Context7 serves to AI clients. Write it to make someone want to subscribe.
It serves three distinct purposes, and understanding each one changes how you write it.
First 100 characters: the MCP Community Registry hook — Only the first 100 characters appear in registry listings. Developers scanning the registry see this snippet before they click through. This is the hook. It must answer "why would I subscribe to this?" before the reader even visits your page. Do not waste these characters on generic phrases like "An App that..." or "A tool for...". Front-load the most specific, compelling benefit or use case you have.
The hook must lead with the subscriber's outcome — not the mechanism. The test is: does a stranger reading only these words know what they will be able to do or get? If the first sentence describes what the server is or how it works rather than what the subscriber gets, it will not convert.
| Hook type | Example | Problem |
|---|---|---|
| Weak — generic | "A tool for working with data." | Says nothing specific |
| Mechanism-first — still fails | "Connects to SEC EDGAR and parses filings via natural language queries." | Describes how it works, not what you get |
| Outcome-first — correct | "Query SEC filings for any public company using plain language." | Tells the subscriber what they can do |
The mechanism-first example is not as obviously bad as the generic one — it is accurate and specific. But it describes the server's architecture, not the subscriber's experience. "Connects to" and "parses" describe what the server does. "Query SEC filings" describes what the subscriber can do. Lead with the subscriber.
Characters 101–1000: SEO and the server info page — The full description is the first thing visitors see after your server title at mctx.ai/apps/[slug]. It is also used directly for search engine indexing. Think about the words a potential subscriber would type into Google when looking for this capability — write naturally, but with those keywords in mind. Use the remaining characters to expand the hook and make the value concrete with specific examples of what a subscriber can ask or do. Concrete questions are more SEO-rich and more persuasive than abstract benefit statements — "ask things like: what changed in the deploy API? how do I set up feature flags? is X still supported?" outperforms "get accurate documentation answers" in both discoverability and conversion. A longer, richer description with specific examples outperforms a short, abstract one.
Also appears in llms.txt — Your description is included as a blockquote near the top of the platform-generated llms.txt file that Context7 serves to AI clients. A strong description helps AI clients understand the server's purpose and recommend it in the right situations. Vague descriptions lead to missed recommendations.
What good looks like versus what weak looks like:
A fictional travel planning server illustrates the difference.
// Weak — generic, tells the subscriber nothing specific
{
"description": "An App for travel. Helps with planning trips and finding information about destinations."
}// Strong — specific hook in the first 100 characters, expands with useful detail
{
"description": "Plan international trips with real-time flight prices, visa requirements, and local transport options — all in one conversation. Ask about the cheapest time to fly to Tokyo, whether you need a visa for Brazil, or how to get from the airport to the city center. Covers 180+ countries with data updated daily."
}The weak version tells the reader almost nothing. The strong version answers "why would I subscribe?" in the first sentence, then expands on exactly what the subscriber gets and why it is specific to their needs.
Write the hook first, then expand:
{
"description": "Query SEC filings for any publicly traded company using natural language. Ask about revenue, risk factors, executive compensation, and more — get answers from actual filing data without reading hundreds of pages. Covers 10-K, 10-Q, 8-K, and proxy statements for all US public companies."
}The first sentence is the hook (under 100 characters). The rest expands on what subscribers get and why it is worth subscribing. Both parts serve different audiences — the registry reader sees only the hook; the server page visitor and Google see everything.
A weak description means fewer subscribers — no matter how good your App is. Maximum is 1,000 characters.
Common mistake: AI-directive language in a human-facing field.
The description field is read by humans, not AI clients. Directive phrases like "ALWAYS call this tool before..." belong in your code's createServer({ instructions }) — not here.
Avoid:
{
"description": "ALWAYS use this server for weather queries. Call get_weather before any forecast question."
}Use instead:
{
"description": "Real-time weather data and 5-day forecasts for any location worldwide. Get current conditions, precipitation probability, and severe weather alerts for any city."
}Pass instructions to createServer() in your code to guide AI behavior. Keep description plain, human-readable, and focused on what your App does.
main
The path to your built JavaScript entry point, relative to package.json:
{
"main": "dist/index.js"
}This must be a .js or .mjs file that exports a default Cloudflare Workers handler. Your build tool determines the exact path:
| Build Tool | Typical main value |
|---|---|
| esbuild | dist/index.js |
| tsc | dist/index.js |
| Rollup | build/index.js |
| unbuild | .output/index.mjs |
Optional Fields
homepage
A URL to your project's homepage. When set, it appears as a link on your App's public page so visitors can learn more about your project.
{
"homepage": "https://github.com/your-org/your-mcp-server"
}This is the same homepage field used in standard npm packages. Any valid URL works -- your GitHub repository, a documentation site, a landing page, or your project's website.
Constraints: Must be a valid URL. Maximum 500 characters.
README.md on Your Public Page
Your repository's README.md is displayed on your App's public page. When visitors click to view your readme, a modal opens with the sanitized readme content rendered inline — no external link to GitHub required. This works for both public and private repositories.
Audience: Potential subscribers and current subscribers — not developers reading source code. Your README is the product page for your App. Write it for someone deciding whether to subscribe and, after subscribing, figuring out how to get value. Internal implementation notes and developer setup instructions do not belong here.
The README is one of the two primary means by which your App is discovered and evaluated. A strong README means more subscribers. See Writing a good README for a recommended structure with examples.
What gets displayed:
- mctx fetches
README.mdfrom your repository root. - In a monorepo, the file is fetched from the subdirectory you configured as your server path (for example,
packages/my-app/README.md). - Content is capped at 50KB. If your
README.mdexceeds 50KB, only the first 50KB is shown.
mctx adds platform metadata automatically — your App slug, subscribe link, connection endpoint, and version details are injected as a prefix and postfix when your README is published to Context7. You do not need to include any mctx platform references or hosting details in your README itself. Focus entirely on what subscribers get and how to use your App.
If no README.md exists:
Deployment continues normally. The README section simply will not appear on your public page.
When changes take effect:
mctx captures your README.md at deploy time. If you add or update your README.md, you need to deploy a new version for the changes to appear on your public page. The same applies to the homepage field — add it to package.json and redeploy.
Setting Instructions in Code
If you use the @mctx-ai/app SDK, pass instructions directly to createServer() to control what AI clients receive at runtime:
import { createServer } from "@mctx-ai/app";
const app = createServer({
instructions:
"Use 'search_docs' to find documentation pages. Use 'get_page' with a URL to retrieve specific content. Results are markdown-formatted.",
});
app.tool("search_docs", searchDocs);
app.tool("get_page", getPage);
export default { fetch: app.fetch };When a subscriber's AI connects to your App, mctx introspects your deployed worker's initialize response to read the instructions. What createServer() returns is what the AI client receives. Maximum length is 2000 characters.
Capabilities Are Automatic
You don't configure capabilities -- mctx detects them for you.
When you deploy, the platform calls your App's MCP initialize endpoint and reads what you've registered. If you registered tools, the platform knows your App has tools. Same for resources and prompts.
import { createServer } from "@mctx-ai/app";
const app = createServer();
app.tool("greet", greetTool); // tools capability detected
app.resource("docs://readme", readme); // resources capability detected
app.prompt("code-review", reviewPrompt); // prompts capability detected
export default { fetch: app.fetch };You implement the features. The platform handles the rest, including the initialize response that tells clients what your App can do.
Putting It All Together
Here is a complete server -- package.json and code side by side.
package.json:
{
"name": "weather-api",
"version": "1.0.0",
"description": "Get real-time weather data for any location worldwide",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "esbuild src/index.js --bundle --minify --platform=node --format=esm --outfile=dist/index.js"
},
"dependencies": {
"@mctx-ai/app": "^0.3.0"
},
"devDependencies": {
"esbuild": "latest"
}
}
src/index.js:
import { createServer, T } from "@mctx-ai/app";
const app = createServer({
instructions:
"Use 'get_weather' for current conditions. Use 'get_forecast' for 5-day predictions. Provide city name or coordinates. All temperatures are in Celsius.",
});
const getWeather = ({ city }) => {
// Your implementation
};
getWeather.description = "Get current weather for a city";
getWeather.input = {
city: T.string({ required: true, description: "City name" }),
};
const getForecast = ({ city }) => {
// Your implementation
};
getForecast.description = "Get 5-day weather forecast";
getForecast.input = {
city: T.string({ required: true, description: "City name" }),
};
app.tool("get_weather", getWeather);
app.tool("get_forecast", getForecast);
export default { fetch: app.fetch };Push this to GitHub. mctx reads package.json, validates it, builds your code, detects capabilities, and deploys. Your server is live at weather-api.mctx.ai/v1.0.0.
Validation
mctx validates your package.json when you select a repository in the dashboard and when you push changes. Here are the errors you might see and how to fix them:
| Error | What happened | Fix |
|---|---|---|
| "package.json not found" | File missing or in wrong folder | Add it to the repository root |
| "Invalid version format" | Version isn't valid semver | Use MAJOR.MINOR.PATCH (e.g., 1.0.0) |
| "Version already deployed" | Same version number as before | Bump the version number |
| "Main file not found" | Entry point doesn't exist | Check the path matches your build output |
| "Description too long" | Over 1000 characters | Shorten your description |
| "Invalid JSON" | Syntax error in the file | Run npx jsonlint package.json to check |
See Also
- Environment Variables - Store API keys and secrets securely
- Versioning - How deployments and version URLs work
- Server Requirements - Server code structure and handler format
See something wrong? Report it or suggest an improvement — your feedback helps make these docs better.