-
Notifications
You must be signed in to change notification settings - Fork 0
Defining Tools
Tools are the primary interface for MCP clients. Each tool has a name, description, input schema, and an action script that runs when the tool is called.
tools:
- name: my-tool # Required. 1-64 chars, [a-zA-Z0-9_-]+
title: My Tool # Optional. Display name for UIs
description: | # Required. Tells the AI when/how to use this tool
Does X when the user asks about Y.
Returns Z in markdown format.
inputSchema: # Required. JSON Schema (type: object)
type: object
properties:
param1:
type: string
description: What this parameter is for
required: [param1]
action: https://example.com/actions/my-tool.v1.js # Required. URL or local file path
# Or for local development:
# action: ./actions/my-tool.v1.js
actionHash: "sha256:64_hex_chars_here" # Required
annotations: # Optional. Hints for MCP clients
readOnlyHint: true
destructiveHint: false
idempotentHint: true
openWorldHint: falseThe inputSchema follows JSON Schema with type: object at the root.
Note: At runtime, git-doc-mcp validates
typeandrequiredfields. Additional JSON Schema features (enum,default,minLength, etc.) are passed through to MCP clients as hints for AI assistants but are not enforced by the server. MCP clients use these hints to generate better tool calls.
String:
properties:
name:
type: string
description: User's nameString with enum (hint for MCP clients, not enforced at runtime):
properties:
format:
type: string
description: "Output format (json, csv, text, or markdown)"
enum: [json, csv, text, markdown]Number:
properties:
limit:
type: number
description: Maximum number of results (1-100)Boolean:
properties:
include_details:
type: boolean
description: Whether to include full detailsString with default (hint for MCP clients, not enforced at runtime):
properties:
branch:
type: string
description: "Git branch name (default: main)"
default: maininputSchema:
type: object
properties:
query: # Required — listed in 'required' array
type: string
description: Search query
language: # Optional — not in 'required' array
type: string
description: Filter by programming language
max_results: # Optional with default
type: number
description: Max results to return (default 10)
required: [query] # Only 'query' is requiredMultiple required parameters:
inputSchema:
type: object
properties:
owner:
type: string
description: Repository owner
repo:
type: string
description: Repository name
path:
type: string
description: File path within the repository
required: [owner, repo, path]All optional parameters:
inputSchema:
type: object
properties:
tag:
type: string
description: Filter by tag
sort:
type: string
description: Sort order
enum: [name, date, relevance]
# No 'required' field — all parameters are optionalNo parameters:
inputSchema:
type: object
properties: {}
# Tool takes no inputAnnotations are hints that help MCP clients understand tool behavior. They don't affect execution.
annotations:
readOnlyHint: true # Tool only reads data, doesn't modify anything
destructiveHint: false # Tool doesn't delete or irreversibly change data
idempotentHint: true # Calling multiple times with same input gives same result
openWorldHint: true # Tool accesses external services (internet)| Annotation | Default | Meaning when true
|
|---|---|---|
readOnlyHint |
false |
Tool only reads, no side effects |
destructiveHint |
false |
Tool may delete or irreversibly change data |
idempotentHint |
false |
Same input always produces same result |
openWorldHint |
false |
Tool accesses external/internet resources |
Read-only data fetcher:
annotations:
readOnlyHint: true
destructiveHint: false
idempotentHint: true
openWorldHint: trueSearch tool (results may change over time):
annotations:
readOnlyHint: true
destructiveHint: false
idempotentHint: false # Results may differ between calls
openWorldHint: trueData modification tool:
annotations:
readOnlyHint: false
destructiveHint: false # Modifies but doesn't destroy
idempotentHint: true # PUT-like: same input, same result
openWorldHint: trueDeletion tool:
annotations:
readOnlyHint: false
destructiveHint: true # Irreversible
idempotentHint: true # Deleting twice has same effect
openWorldHint: trueEvery tool requires a SHA256 hash of its action script for integrity verification.
# macOS/Linux
shasum -a 256 actions/my-tool.v1.js
# Output: a1b2c3d4e5f6... actions/my-tool.v1.js
# Using openssl
openssl dgst -sha256 actions/my-tool.v1.js
# Using Node.js
node -e "const fs=require('fs'); const crypto=require('crypto'); console.log('sha256:'+crypto.createHash('sha256').update(fs.readFileSync('actions/my-tool.v1.js')).digest('hex'))"actionHash: "sha256:a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"Format: sha256: followed by exactly 64 lowercase hexadecimal characters.
You must recompute and update the hash whenever the action script changes. Even a single character change produces a completely different hash. This is by design — it ensures the action code running is exactly what you reviewed.
- 1 to 64 characters
- Allowed: letters, digits, underscores, hyphens (
[a-zA-Z0-9_-]+) - Case-sensitive
# Good: descriptive, kebab-case
- name: fetch-file
- name: search-code
- name: list-issues
- name: get-user-profile
# Good: with namespace prefix
- name: github-search
- name: jira-get-ticket
- name: slack-send-message
# Avoid: too generic
- name: get # Get what?
- name: data # What kind?
- name: do-thing # Not descriptiveThe description field is critical — it's what AI assistants use to decide when to call your tool.
description: |
Search for code patterns across all files in a GitHub repository.
Returns matching file paths with line numbers and code snippets.
Supports regex patterns and language filters.
Use this when the user asks to find specific code, function definitions,
or usage patterns in a repository.description: |
Fetch the raw content of a file from a GitHub repository.
Returns the file content as plain text.
Use this to read source code, documentation, or configuration files.
Requires owner, repo, and file path.# Too vague — AI won't know when to use it
description: Gets data from the API
# Too implementation-focused — should describe purpose, not internals
description: Makes a GET request to /api/v2/search endpoint with query parameters
# Missing context about when to use it
description: Returns JSON dataThe examples above are a good start, but there's a critical pattern that dramatically increases how often AI assistants call your tools: explicit trigger conditions.
LLMs decide whether to call a tool by matching the user's intent against the tool description. A description that says "searches code" is informative. A description that says "USE THIS WHEN the user asks to find specific code" is actionable.
The trigger pattern:
description: |
Search for code patterns across all files in a GitHub repository.
Returns matching file paths with line numbers and code snippets.
Supports regex patterns and language filters.
USE THIS WHEN:
- The user asks to find specific functions, imports, or usage patterns
- The user says "search for", "find", "where is", or "grep"
- The user wants to understand how a feature is implemented
- You need to locate code before making changes
DO NOT USE WHEN:
- You already know the exact file path (use get-file instead)
- The user wants to browse directory structure (use list-files instead)
Example: "Find all uses of fetchData" / "Where is the auth middleware?" / "Search for TODO comments"What each section does:
| Section | Purpose |
|---|---|
| First paragraph | What the tool does and what it returns |
USE THIS WHEN: |
Explicit conditions that should trigger tool invocation |
DO NOT USE WHEN: |
Prevents misuse and points to the right alternative |
Example: |
Concrete user queries the LLM can pattern-match against |
Before/after comparison:
# BEFORE: LLM must infer when to use this
description: Gets data from the API. Returns JSON.
# AFTER: LLM knows exactly when and when not to use this
description: |
Fetch user profile data from the Acme API.
Returns the user's name, email, role, and last login as JSON.
USE THIS WHEN:
- The user asks about a specific person ("who is john?", "show me user 42")
- The user needs account details, permissions, or login history
- You need to verify a user exists before performing other operations
DO NOT USE WHEN:
- The user wants to search across all users (use search-users instead)
- The user wants to modify a profile (use update-user instead)
Example: "Get the profile for user alice" / "What role does user 12 have?"A manifest with multiple tools covering different use cases:
tools:
- name: list-documents
title: List Documents
description: |
List all documents in a workspace. Optionally filter by tag or type.
Returns document titles, IDs, and last modified dates.
inputSchema:
type: object
properties:
tag:
type: string
description: Filter by tag name
type:
type: string
description: Filter by document type
enum: [note, article, template, report]
# All optional — lists everything if no filters
action: https://example.com/actions/list-docs.v1.js
actionHash: "sha256:..."
annotations:
readOnlyHint: true
idempotentHint: false # List may change between calls
openWorldHint: true
- name: get-document
title: Get Document
description: |
Fetch the full content of a document by ID.
Returns the document body in markdown format with metadata.
inputSchema:
type: object
properties:
id:
type: string
description: Document ID
required: [id]
action: https://example.com/actions/get-doc.v1.js
actionHash: "sha256:..."
annotations:
readOnlyHint: true
idempotentHint: true
openWorldHint: true
- name: search-documents
title: Search Documents
description: |
Full-text search across all documents in the workspace.
Returns matching documents ranked by relevance with highlighted snippets.
inputSchema:
type: object
properties:
query:
type: string
description: Search keywords or phrase
max_results:
type: number
description: Maximum results to return (default 10, max 50)
required: [query]
action: https://example.com/actions/search-docs.v1.js
actionHash: "sha256:..."
annotations:
readOnlyHint: true
idempotentHint: false
openWorldHint: true