Connect tools deliberately through APIs and MCP
From task definition to integration acceptance: a checked tool core with explicit transport, authorization and action boundaries.

An AI assistant may need to find a service description, look up a record or prepare a draft. That does not require unrestricted access to an entire business database or its user interface. A bounded tool can expose exactly the operation needed. APIs and the Model Context Protocol help describe such capabilities and make them available to an assistant.
The important work extends beyond establishing a connection. A tool needs an understandable purpose, checked inputs, appropriate permissions and an accurate result. This guide separates those layers and provides a completely executable read operation. It uses a fictional public teaching catalogue without network access. Its 72 executed checks apply to that function and result wrapper. They do not establish a complete MCP server or production authentication flow. Sources were reviewed on 9 November 2026.
Start with a concrete task
The first question is what the person wants to achieve with the assistant. Enabling system access is too vague. Reading a public service description using a known code and chosen language is a testable task. Its result and boundaries can be specified before any model integration exists. That specification provides a basis for accepting or rejecting proposed capabilities.
A useful tool contract distinguishes related tasks. Reading a description, saving a draft and sending a message have different consequences. Combining them in one tool with numerous hidden modes complicates selection and control. It also complicates evaluation. A reading tool should not suddenly send something because of an unobtrusive parameter that changes its fundamental behavior.
The example has two service codes and two languages. It does not approximately complete unknown codes. An invalid request does not receive an invented substitute. A real application can turn that rejection into a useful question or error message. It should not pretend a different service is the one requested merely to produce a successful looking response.
Distinguish business logic, interface and model
The business function performs a specified operation. An adapter translates a call into the conventions of an existing system. The model can propose an operation and supply arguments. These roles need independent evaluation. Selecting the right tool does not prove that the supplied identifiers are valid or that the requester is entitled to use them.
An existing HTTP API can supply the business capability. MCP provides a standardized exchange of capabilities and calls. It does not automatically replace internal business rules or the data model. Before connecting an application, understand its existing read and write paths. Adding a protocol layer cannot repair an unclear permission model in the target system.
The official OpenAI guide to MCP servers addresses tool descriptions, schemas and authorization together. Our design draws a clear boundary from that: a description supports selection. Whether private information may be released must be determined by checked server-side permissions rather than a model's assumption about the person making the request.
Treat MCP as a versioned protocol
A technical example should identify its revision. The current MCP versioning and compatibility description at the review date distinguishes modern requests beginning with revision 2026-07-28 from earlier forms. Modern requests carry protocol context per request. Legacy revisions through 2025-11-25 use an initialization exchange. These approaches should not be combined invisibly.
An existing client may still support an earlier form. Saying that MCP works is therefore an insufficient compatibility statement. Record the client, server library and revision actually used. An example with an older initialization exchange is not automatically wrong, but it does not fully describe every current integration. Compatibility needs a specific pair of implementations and a tested contract.
The base protocol for revision 2026-07-28 defines further message and metadata requirements. Protocol version and client capabilities are required per request in that revision. Client name and version are recommended rather than mandatory. Such self-reported information is not a verified identity for security decisions. The module below deliberately does not recreate a complete message server.
Make tools discoverable and unambiguous
A tool name should communicate the action. Its description explains when the operation is useful and where its limits lie. The example is named get_public_service. Its description identifies a bounded fictional catalogue. It claims neither access to current customer offers nor access to actual agency records. That distinction helps prevent an assistant from presenting teaching data as a real business result.
The MCP tools specification describes tool listings, calls and input and output schemas. A client can use them to identify the expected parameters. A schema has practical effect only when the executing side handles inputs accordingly. An attractive description is not input validation and should not be accepted as a substitute for inspecting actual behavior.
| Contract element | Purpose | Example boundary |
|---|---|---|
| Name and description | Support suitable selection | Read one public service description |
| Input schema | Describe accepted arguments | Two codes and two languages |
| Business function | Check inputs and determine the result independently | Reject additional parameters |
| Output contract | Return an understandable result | Code, language, title and summary |
Separate reading, authorization and action approval
A public service description does not require private identity in this example. A tool accessing a customer's order would have different requirements. It would need to identify the requester reliably and check access to that particular order. A customer number supplied by a model does not establish that the person may see the corresponding information.
Explicit approval of an action does not replace that check. Someone can act only within their actual permissions. Conversely, a valid login does not establish that every consequential proposal should execute without further examination. Business validity, account permissions and the particular requested step need to agree. Each answers a different question about the operation.
The OpenAI documentation for MCP in the Responses API describes an integration-specific approval flow for tool calls. Distinguish that from authorization in the target system. Approval in an interface must not conceal missing backend access control. The concrete approach depends on the integration and the consequences of the task rather than a universal assumption about all clients.
Interpret side-effect hints accurately
Tool metadata can describe reading, changing state or accessing open-ended external entities. These hints should match real behavior. A tool sending a message or starting a queue is not read-only just because it does not directly modify a database row. Indirect consequences matter too. Review the entire operation rather than choosing a label based on one implementation detail.
Our function reads only from a fixed catalogue held by the module. It neither changes that catalogue nor calls an external address. Its declared hints fit those limits. Changing it later to save records or search the web would require reviewing both the description and annotations. Keeping an old label does not make a new side effect harmless or adequately described.
Hints are not technical enforcement. The executing application must independently enforce permissions and allowed targets. An unknown server's favorable self-description does not establish trust. Acceptance should begin by examining actual behavior, followed by checking whether metadata represents that behavior accurately. The order matters because a correct description of an unsafe operation still leaves the operation unsafe for an unsuitable use.
The complete bounded tool function
The following JavaScript module needs no external library. It contains the tool contract, fictional catalogue, input checks and result construction. A valid argument combination receives a new result object. This prevents a caller from accidentally changing internal catalogue state by mutating an object returned by the function.
export const toolDefinition = {
name: 'get_public_service',
description: 'Liest eine öffentliche, fiktive Leistungsbeschreibung aus dem begrenzten Lehrkatalog.',
inputSchema: {
type: 'object',
properties: {code: {type: 'string', enum: ['web', 'automation']}, locale: {type: 'string', enum: ['de', 'en']}},
required: ['code', 'locale'],
additionalProperties: false
},
outputSchema: {
type: 'object',
properties: {code: {type: 'string'}, locale: {type: 'string'}, title: {type: 'string'}, summary: {type: 'string'}},
required: ['code', 'locale', 'title', 'summary'],
additionalProperties: false
},
annotations: {readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false}
};
const catalogue = {
web: {
de: {title: 'Fiktive Leistung: Webseite', summary: 'Lehrangebot für die Planung einer verständlichen Unternehmenswebseite.'},
en: {title: 'Fictional service: website', summary: 'Teaching offer for planning an understandable business website.'}
},
automation: {
de: {title: 'Fiktive Leistung: Automatisierung', summary: 'Lehrangebot zur Beschreibung und Prüfung eines begrenzten Arbeitsablaufs.'},
en: {title: 'Fictional service: automation', summary: 'Teaching offer for specifying and evaluating a bounded workflow.'}
}
};
export function getPublicService(args) {
if (!args || typeof args !== 'object' || Array.isArray(args) ||
Object.keys(args).sort().join(',') !== 'code,locale' ||
!['web', 'automation'].includes(args.code) || !['de', 'en'].includes(args.locale)) {
throw new TypeError('Invalid service arguments');
}
const item = catalogue[args.code][args.locale];
return {code: args.code, locale: args.locale, title: item.title, summary: item.summary};
}
export function serviceToolResult(args) {
const value = getPublicService(args);
return {
resultType: 'complete',
structuredContent: value,
content: [{type: 'text', text: JSON.stringify(value)}],
isError: false
};
}
The input check accepts exactly the two intended properties. Unknown codes, unsupported languages and additional values are rejected. In particular, the function does not interpret arbitrary paths, URLs or commands. The fixed catalogue is an intentional task boundary. A generic operation to retrieve arbitrary information would create a substantially larger validation and authorization problem.
This module is a business function with a result wrapper. Transport, protocol discovery, server description, private-account access checks and a particular client integration are separate responsibilities of a complete adapter. An input exception caught there needs mapping to an appropriate protocol response. Throwing a JavaScript exception alone is not a complete implementation of MCP error handling.
Trace one concrete result
For code: "web" and locale: "de", the function returns an explicitly fictional website service. Structured content contains the same values as the textual JSON representation. Both are constructed from the same object. They should not communicate different identifiers or descriptions. The German teaching data remains unchanged in this language version.
{
"resultType": "complete",
"structuredContent": {
"code": "web",
"locale": "de",
"title": "Fiktive Leistung: Webseite",
"summary": "Lehrangebot für die Planung einer verständlichen Unternehmenswebseite."
},
"content": [
{
"type": "text",
"text": "{\"code\":\"web\",\"locale\":\"de\",\"title\":\"Fiktive Leistung: Webseite\",\"summary\":\"Lehrangebot für die Planung einer verständlichen Unternehmenswebseite.\"}"
}
],
"isError": false
}
The wrapper uses resultType: "complete" for the modern revision discussed here. An SDK or adapter supporting a different revision must honor that revision's contract. This individual wrapper is neither the complete JSON-RPC response nor all metadata required by an actual connection. Do not present it unchanged as evidence that a server integration has succeeded.

Read the 72 executed checks correctly
The saved test run examines all four valid combinations of code and language. It checks repeatable results, exact shape, returned identifiers, agreement between text and structured content and completion status. It also mutates a returned object and verifies that a later query still returns the unchanged catalogue. These expectations connect the claimed read operation to observable behavior.
Twenty invalid argument variants are exercised against both the business function and the wrapper. They cover missing fields, incorrect types, additional targets and special property names. Four further expectations concern the closed schemas and declared reading behavior. Altogether, 72 expectations passed. This establishes the examined module properties rather than universal security for an MCP installation.
No language model was called, no OAuth connection was established and no production service was queried. Real integration needs further checks: compatible protocol revision, actual transport errors, unauthorized accounts, bounded execution times and behavior of the chosen client. Separating those forms of evidence prevents a successful function test from being mistaken for complete integration acceptance.
Handle errors and repeated attempts according to the task
Invalid arguments, missing permission, a nonexistent record and an unavailable target system are different situations. Do not render all of them as an empty successful result. An understandable error helps the assistant and the person choose a sensible next step. Internal credentials and confidential diagnostic content are not needed in that response and should not be exposed through it.
Repeated attempts become particularly important for writes. After a timeout, it may be unclear whether an action happened. Blindly trying again can create duplicate records or messages. The operation then needs its own business mechanism for identifying requests and checking their state. An idempotency hint in metadata does not implement that mechanism or establish its guarantees.
The catalogue's situation is simpler: reading has no such write effect. Repeated valid calls return the same content. That says nothing about a future messaging or payment integration. Every additional operation needs its own contract and counterexamples rather than automatically inheriting the acceptance of this reading tool. Scope should grow through explicit decisions, not incidental code reuse.
Limit credentials and untrusted external content
The MCP security guidance describes risks including unvalidated token forwarding and unintended network destinations. For architecture, a token needs validation for its actual recipient and purpose. It belongs neither in the prompt nor in a freely visible tool result. General access to arbitrary addresses requires additional protections beyond a useful name and schema.
Retrieved content can be unreliable as well. If an external record tells the assistant to execute a different action, that text is initially data, not a new permission. The tool contract and responsible application determine what operation is allowed. Investigate that boundary through targeted examples when external content is introduced. Do not let a document silently redefine the credentials or scope used by the application.
This artificial catalogue contains only fixed neutral text. It does not perform a real attack test against a provider. A later extension should include ordinary requests, misleading source material and unauthorized access attempts. Blocking one example sentence does not justify saying every manipulation is impossible. Evidence should describe the cases and components actually examined.
Accept a real integration in stages
Begin with the smallest useful reading workflow and test it independently of the model. Next connect the adapter to its actual client. Then investigate whether the assistant selects the correct tool and supplies valid arguments across different phrasings. This sequence distinguishes selection problems from business or transport problems and makes corrections easier to verify.
Private-data acceptance needs both an authorized and an unauthorized situation. Also try a valid identifier belonging to another account or responsibility area. A correctly formatted argument can still be forbidden. The executing application must recognize that difference. Testing only through an administrator with broad access would not adequately investigate that boundary.
Changes to names, parameters or result meaning need an identifiable version and targeted repeat checks. A client may cache earlier information. Existing workflows should not silently receive a changed contract. Document the combination actually tested and how a faulty version can be rolled back. Additional tools are new capabilities to assess, even when they share code with an existing one.
Plan result scope and operational evidence deliberately
An output should contain what the task needs. The example requires code, language, title and summary. A real integration may need additional business fields. Internal account identifiers, complete logs and access information do not become useful merely because they exist in the target system. The output contract should explain what the calling side actually receives.
Operational diagnostics also need a defined scope. A tool name, outcome category and suitable technical correlation may help investigate failures. That does not justify permanently recording every argument and response. They can contain confidential material. Decide which diagnostics are necessary, who may access them and how they relate to the underlying task.
A remote API's timing and failure behavior form part of its contract. A timeout limits waiting; it does not prove the target aborted the operation. For changes, preserve the distinction between an unknown outcome and confirmed nonexecution. That prevents both invented success messages and unnecessary duplicate actions. Repeat known counterexamples when evaluating an update rather than relying on one successful demonstration.
Checklist for a traceable tool integration
- Describe one concrete task and its correct result.
- Separate reading, drafting and consequential actions sensibly.
- Evaluate model selection, adapter and business function independently.
- Document client, library and protocol revision.
- Restrict server-side inputs to allowed properties and values.
- Protect private data through validated permissions on every request.
- Describe side effects accurately and actually constrain them.
- Keep textual and structured results consistent.
- Handle errors, timeouts and repeats without invented success.
- Keep credentials and unnecessary confidential material out of results.
- Test valid, invalid and unauthorized situations separately.
- Distinguish function evidence from complete server acceptance.
A useful integration makes a capability understandable and bounded. The person receives an accurate result while the executing application retains clear responsibilities. MCP can standardize the exchange. Quality comes from the appropriate task, enforced limits and acceptance of the components actually deployed.







