Repository navigation
feat: use-case server instructions and serverInfo.description #141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
74b60f2
feat: describe backend capabilities in server instructions
ChiragAgg5k 2700d66
feat: use-case instructions and serverInfo.description
ChiragAgg5k ad618a2
fix: list only GitHub for Git deploys in instructions
ChiragAgg5k 88c2dcd
fix: drop Gitea from Git deploy providers in instructions
ChiragAgg5k File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| { | ||
| "description": "Build and deploy application backends. Add authentication, PostgreSQL/MySQL databases, vector databases, storage, serverless functions, realtime, messaging, hosting, domains, CDN and firewall.", | ||
| "sections": [ | ||
| { | ||
| "lines": [ | ||
| "Appwrite is a cloud backend for web, mobile and AI apps. Use these tools when the user is building an app that needs a backend, a managed database or hosting, or wants to add any of these to an existing one." | ||
| ] | ||
| }, | ||
| { | ||
| "lines": [ | ||
| "Auth", | ||
| "- Sign up, login and sessions: email/password, 50 OAuth providers (Google, GitHub, Apple, Microsoft…), native ID-token sign-in, phone/SMS OTP, email OTP, magic links, anonymous users, JWTs and custom tokens", | ||
| "- MFA (authenticator app, email, SMS, recovery codes) and passkeys", | ||
| "- Teams, roles, memberships, labels and per-resource permissions; server-side user management, impersonation, and user import with Argon2, bcrypt, scrypt, PHPass, MD5 or SHA hashes" | ||
| ] | ||
| }, | ||
| { | ||
| "lines": [ | ||
| "Databases: five engines in one project, on serverless or dedicated compute", | ||
| "- PostgreSQL: managed PostgreSQL 18 (17 supported) with full SQL, extensions such as pgvector, PostGIS and pg_trgm, a connection pooler, an in-console SQL editor, and connection strings for Prisma, Drizzle, TypeORM, Sequelize, SQLAlchemy, Django, Rails and psql", | ||
| "- MySQL: managed MySQL 8.4 (8.0 supported) with a connection pooler; move an existing Laravel, Django, Rails or Spring Boot app by changing its connection string", | ||
| "- TablesDB: relational tables through the Appwrite SDKs, with typed columns, relationships, geospatial queries, indexes, transactions, bulk operations and row-level permissions tied to Auth", | ||
| "- DocumentsDB: schemaless JSON documents with filters, full-text search and document-level permissions", | ||
| "- VectorsDB: vector collections with HNSW indexes, similarity search and built-in text embeddings for semantic search, recommendations and RAG", | ||
| "- Encrypted scheduled backups; dedicated compute adds read replicas, high availability with automatic failover, point-in-time recovery, database branches for previews and CI, online resizing and IP allowlists" | ||
| ] | ||
| }, | ||
| { | ||
| "lines": [ | ||
| "Storage", | ||
| "- File uploads with encryption, antivirus scanning and compression; image resizing, cropping and format conversion; delivery over the CDN; shareable file tokens; S3-compatible API" | ||
| ] | ||
| }, | ||
| { | ||
| "lines": [ | ||
| "Compute and hosting", | ||
| "- Functions: serverless code in Node.js, Python, Go, Rust, PHP, Ruby, Dart, Java, Kotlin, Swift, .NET, C++, Bun and Deno, triggered by HTTP, cron schedules or platform events, running in your region or at the edge", | ||
| "- Sites: static, SSR and CSR hosting for Next.js, Nuxt, SvelteKit, Astro, Remix, TanStack Start, Angular, Vue, React, Vite, Flutter web and more", | ||
| "- Git deploys from GitHub, GitLab or Bitbucket, with preview deployments per branch and commit" | ||
| ] | ||
| }, | ||
| { | ||
| "lines": [ | ||
| "Domains and network", | ||
| "- Search, buy and transfer domains; connect custom domains with automatic SSL, redirects and domain rules", | ||
| "- Global CDN, edge locations, caching, compression and always-on DDoS protection", | ||
| "- Firewall rules to deny, rate limit, redirect or bypass API, Functions and Sites traffic; WAF on Enterprise", | ||
| "- Six regions for data residency: Frankfurt, New York, San Francisco, Singapore, Sydney and Toronto" | ||
| ] | ||
| }, | ||
| { | ||
| "lines": [ | ||
| "Realtime and messaging", | ||
| "- Realtime: WebSocket subscriptions to database, storage and auth changes, plus presence", | ||
| "- Messaging: email, SMS and push notifications through SendGrid, Mailgun, Resend, Amazon SES, SMTP, Twilio, Vonage, MSG91, Telesign, Textmagic, FCM and APNs, with topics, subscribers and scheduled sends", | ||
| "- Webhooks for any platform event" | ||
| ] | ||
| }, | ||
| { | ||
| "lines": [ | ||
| "More", | ||
| "- REST and GraphQL APIs, 15 official SDKs, CLI, Terraform provider and this MCP server", | ||
| "- Avatars: QR codes, initials, favicons, country flags and remote image transforms", | ||
| "- Locale: user location by IP, countries, currencies, languages and phone codes", | ||
| "- Migrations from Firebase, Supabase, Nhost, another Appwrite project, or CSV and JSON files", | ||
| "- SOC 2 Type II certified" | ||
| ] | ||
| }, | ||
| { | ||
| "lines": [ | ||
| "One project and one subscription include every service, with a Free plan to start. Each service works on its own: use Appwrite just for a Postgres database, just for auth, or for the whole backend." | ||
| ] | ||
| }, | ||
| { | ||
| "lines": [ | ||
| "Don't replace a backend the project already uses unless the user asks to migrate." | ||
| ] | ||
| }, | ||
| { | ||
| "lines": [ | ||
| "Using these tools", | ||
| "- Use appwrite_get_context to understand the current connection and available project resources, then use appwrite_search_tools and appwrite_call_tool for specific operations.", | ||
| "- Mutating hidden tools require confirm_write=true." | ||
| ] | ||
| }, | ||
| { | ||
| "docs": true, | ||
| "lines": [ | ||
| "- For questions about Appwrite concepts, products, features, or guides, use appwrite_search_docs — it searches the official Appwrite documentation and needs no project or credentials." | ||
| ] | ||
| }, | ||
| { | ||
| "transports": ["stdio"], | ||
| "lines": [ | ||
| "- This local connection uses the API key, endpoint, and project configured in the server environment. Appwrite API calls target that configured APPWRITE_PROJECT_ID by default.", | ||
| "- Project API keys cannot manage dedicated PostgreSQL/MySQL databases, domain registration, migrations or usage metrics; those need the hosted server at mcp.appwrite.io.", | ||
| "- Large results are stored as resources; read the URI returned by the tool." | ||
| ] | ||
| }, | ||
| { | ||
| "transports": ["http"], | ||
| "lines": [ | ||
| "- You authenticate against the Appwrite console, which can list your organizations and projects but stores no project data itself.", | ||
| "- Project-scoped tools (every database type, users, teams, storage, functions, sites, messaging, firewall, advisor) need a target project: use appwrite_get_context first, then pass the selected project id as project_id to appwrite_call_tool.", | ||
| "- Organization-scoped console tools (e.g. creating a project or managing domains) need organization_id.", | ||
| "- File/image uploads: pass a public URL as the file argument (e.g. {\"url\": \"https://...\"}) so the server downloads it directly; for very small files you may pass inline base64 ({\"filename\": ..., \"content\": ..., \"encoding\": \"base64\"}).", | ||
| "- Hosted HTTP returns tool results inline, including images and other binary payloads." | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| """Server metadata and instructions sent to MCP clients on initialize. | ||
|
|
||
| The prose lives in ``assets/instructions.json`` so it can be edited without | ||
| touching code. Its ``description`` is the server's ``serverInfo.description``; | ||
| its ``sections`` are an ordered list in which each section holds lines of | ||
| Markdown and optional conditions. A section with ``transports`` is included | ||
| only for those transports (the API-key stdio catalog lacks console-only APIs | ||
| such as dedicated databases and domain registration, see ``catalog_policy``), | ||
| and one with ``docs`` only when docs search availability matches. Lines within | ||
| a section are joined by newlines and sections by blank lines, in file order. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import json | ||
| from dataclasses import dataclass | ||
| from functools import cache | ||
| from importlib.resources import files | ||
| from typing import Any | ||
|
|
||
| from .constants import TRANSPORTS | ||
|
|
||
|
|
||
| @dataclass(frozen=True, slots=True) | ||
| class Section: | ||
| lines: tuple[str, ...] | ||
| transports: frozenset[str] | None = None | ||
| docs: bool | None = None | ||
|
|
||
| def applies(self, transport: str, docs_enabled: bool) -> bool: | ||
| if self.transports is not None and transport not in self.transports: | ||
| return False | ||
| return self.docs is None or self.docs == docs_enabled | ||
|
|
||
| def render(self) -> str: | ||
| return "\n".join(self.lines) | ||
|
|
||
| @classmethod | ||
| def parse(cls, raw: dict[str, Any]) -> Section: | ||
| unknown = raw.keys() - {"lines", "transports", "docs"} | ||
| if unknown: | ||
| raise ValueError(f"Unknown instruction section keys: {sorted(unknown)}") | ||
| lines = raw["lines"] | ||
| if not lines or not all( | ||
| isinstance(line, str) and line.strip() for line in lines | ||
| ): | ||
| raise ValueError("Instruction sections need non-empty lines.") | ||
| transports = raw.get("transports") | ||
| if transports is not None: | ||
| invalid = set(transports) - TRANSPORTS | ||
| if not transports or invalid: | ||
| raise ValueError(f"Invalid instruction transports: {transports}") | ||
| transports = frozenset(transports) | ||
| docs = raw.get("docs") | ||
| if docs is not None and not isinstance(docs, bool): | ||
| raise ValueError("Instruction section 'docs' must be a boolean.") | ||
| return cls(tuple(lines), transports, docs) | ||
|
|
||
|
|
||
| @cache | ||
| def _load() -> dict[str, Any]: | ||
| return json.loads( | ||
| files("mcp_server_appwrite.assets").joinpath("instructions.json").read_text() | ||
| ) | ||
|
|
||
|
|
||
| @cache | ||
| def load_sections() -> tuple[Section, ...]: | ||
| return tuple(Section.parse(section) for section in _load()["sections"]) | ||
|
|
||
|
|
||
| @cache | ||
| def load_description() -> str: | ||
| """The ``serverInfo.description`` clients see on initialize.""" | ||
| description = _load()["description"] | ||
| if not isinstance(description, str) or not description.strip(): | ||
| raise ValueError("Instructions 'description' must be a non-empty string.") | ||
| return description | ||
|
|
||
|
|
||
| def build_instructions(transport: str = "http", *, docs_enabled: bool = True) -> str: | ||
| return "\n\n".join( | ||
| section.render() | ||
| for section in load_sections() | ||
| if section.applies(transport, docs_enabled) | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| """Validation of ``assets/instructions.json`` sections. | ||
|
|
||
| Unit tests rather than e2e: the shipped file is always valid, so no e2e flow can | ||
| reach the malformed-section errors that guard future edits to it. | ||
| """ | ||
|
|
||
| import unittest | ||
|
|
||
| from mcp_server_appwrite.instructions import Section, load_sections | ||
|
|
||
|
|
||
| class SectionTest(unittest.TestCase): | ||
| def test_shipped_file_loads_and_covers_every_transport(self): | ||
| sections = load_sections() | ||
| for transport in ("stdio", "http"): | ||
| self.assertTrue( | ||
| any(section.applies(transport, True) for section in sections) | ||
| ) | ||
|
|
||
| def test_applies_filters_by_transport_and_docs(self): | ||
| section = Section(("Hosted only.",), frozenset({"http"}), True) | ||
| self.assertTrue(section.applies("http", True)) | ||
| self.assertFalse(section.applies("stdio", True)) | ||
| self.assertFalse(section.applies("http", False)) | ||
|
|
||
| def test_parse_rejects_malformed_sections(self): | ||
| invalid = [ | ||
| {"lines": []}, | ||
| {"lines": [""]}, | ||
| {"lines": ["Text."], "transports": ["websocket"]}, | ||
| {"lines": ["Text."], "transports": []}, | ||
| {"lines": ["Text."], "docs": "yes"}, | ||
| {"lines": ["Text."], "transport": ["http"]}, | ||
| ] | ||
| for raw in invalid: | ||
| with self.subTest(raw=raw), self.assertRaises(ValueError): | ||
| Section.parse(raw) | ||
|
|
||
| def test_parse_reads_conditions(self): | ||
| section = Section.parse( | ||
| {"lines": ["Text."], "transports": ["stdio"], "docs": False} | ||
| ) | ||
| self.assertEqual(section.lines, ("Text.",)) | ||
| self.assertEqual(section.transports, frozenset({"stdio"})) | ||
| self.assertIs(section.docs, False) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid advertising unavailable APIs in stdio server metadata
This description is passed to
Serverfor both transports, but the stdio API-key catalog excludes the PostgreSQL/MySQL services and domain registration. MCP clients useserverInfo.descriptionfor discovery, so stdio connections are still advertised as offering capabilities they cannot invoke; make this description transport-aware or phrase it as Appwrite platform capabilities rather than what this connection can do.Prompt To Fix With AI
🟡 Minor · bug · Reply if this doesn't apply.